2008-06-02 17:41:35 -04: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 - 2014 David Robillard < d @ drobilla . net >
* 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 >
*
* 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
# include <cmath>
# include <cerrno>
# include <algorithm>
# include <unistd.h>
2019-09-17 20:26:03 -04:00
# include <boost/algorithm/string/erase.hpp>
2019-03-13 20:11:10 -04:00
2019-09-17 20:26:03 -04:00
# include "pbd/i18n.h"
2009-02-25 13:26:51 -05:00
# include "pbd/error.h"
2009-12-03 21:15:12 -05:00
# include "pbd/enumwriter.h"
2020-03-29 08:56:22 -04:00
# include "pbd/pthread_utils.h"
2008-06-02 17:41:35 -04:00
2012-07-25 13:48:55 -04:00
# include <glibmm/threads.h>
2008-06-02 17:41:35 -04:00
2020-12-07 19:13:30 -05:00
# include "temporal/tempo.h"
2020-11-20 00:16:42 -05:00
2009-02-25 13:26:51 -05:00
# include "ardour/audioengine.h"
# include "ardour/auditioner.h"
2009-10-23 20:39:28 -04:00
# include "ardour/butler.h"
2012-05-24 02:09:29 -04:00
# include "ardour/cycle_timer.h"
2009-11-30 11:12:13 -05:00
# include "ardour/debug.h"
2017-07-20 17:53:56 -04:00
# include "ardour/disk_reader.h"
2012-05-24 02:09:29 -04:00
# include "ardour/graph.h"
2022-04-14 07:24:03 -04:00
# include "ardour/io_plug.h"
2012-05-24 02:09:29 -04:00
# include "ardour/port.h"
2010-04-13 16:48:33 -04:00
# include "ardour/process_thread.h"
2022-04-14 07:24:03 -04:00
# include "ardour/rt_tasklist.h"
2014-04-28 19:58:24 -04:00
# include "ardour/scene_changer.h"
2009-10-23 20:39:28 -04:00
# include "ardour/session.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"
# include "ardour/transport_master_manager.h"
2012-01-27 08:22:55 -05:00
# include "ardour/ticker.h"
2021-11-01 14:09:29 -04:00
# include "ardour/triggerbox.h"
2012-05-24 02:09:29 -04:00
# include "ardour/types.h"
2017-07-15 14:45:49 -04:00
# include "ardour/vca.h"
# include "ardour/vca_manager.h"
2008-06-02 17:41:35 -04:00
2010-06-29 09:47:53 -04:00
# include "midi++/mmc.h"
2008-06-02 17:41:35 -04:00
using namespace ARDOUR ;
using namespace PBD ;
using namespace std ;
2019-09-20 11:38:09 -04:00
# define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::Event (evtype)); }
2021-04-16 01:09:41 -04:00
# define TFSM_ROLL() { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::StartTransport)); }
2019-09-20 11:38:09 -04:00
# define TFSM_STOP(abort,clear) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::StopTransport,abort,clear)); }
2021-06-18 13:21:15 -04:00
# define TFSM_SPEED(speed) { _transport_fsm->enqueue (new TransportFSM::Event (speed)); }
2021-04-28 21:54:51 -04:00
# define TFSM_LOCATE(target,ltd,loop,force) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::Locate,target,ltd,loop,force)); }
2019-09-20 00:33:43 -04:00
2019-03-13 20:11:10 -04:00
2020-11-20 00:16:42 -05:00
void
Session : : setup_thread_local_variables ( )
{
2020-12-07 19:13:30 -05:00
Temporal : : TempoMap : : fetch ( ) ;
2020-11-20 00:16:42 -05:00
}
2008-06-02 17:41:35 -04:00
/** Called by the audio engine when there is work to be done with JACK.
2017-09-18 12:39:17 -04:00
* @ param nframes Number of samples to process .
2008-06-02 17:41:35 -04:00
*/
2011-03-07 14:06:42 -05:00
2008-06-02 17:41:35 -04:00
void
2010-12-03 17:26:29 -05:00
Session : : process ( pframes_t nframes )
2008-06-02 17:41:35 -04:00
{
2021-06-11 17:38:58 -04:00
TimerRAII tr ( dsp_stats [ OverallProcess ] ) ;
2009-04-16 12:02:25 -04:00
if ( processing_blocked ( ) ) {
_silent = true ;
return ;
2022-02-01 15:39:55 -05:00
} else {
_silent = false ;
2009-04-16 12:02:25 -04:00
}
2022-02-01 15:39:55 -05:00
samplepos_t transport_at_start = _transport_sample ;
setup_thread_local_variables ( ) ;
2008-06-02 17:41:35 -04:00
if ( non_realtime_work_pending ( ) ) {
2019-09-22 13:50:51 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " non-realtime work pending: %1 (%2%3%4) \n " , enum_2_string ( post_transport_work ( ) ) , std : : hex , post_transport_work ( ) , std : : dec ) ) ;
2009-10-23 20:39:28 -04:00
if ( ! _butler - > transport_work_requested ( ) ) {
2019-09-17 20:26:03 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " done, waiting? %1 \n " , _transport_fsm - > waiting_for_butler ( ) ) ) ;
butler_completed_transport_work ( ) ;
} else {
2019-09-22 13:50:51 -04:00
DEBUG_TRACE ( DEBUG : : Butler , " doesn't seem to have finished yet (from view of RT thread) \n " ) ;
2009-10-14 12:10:01 -04:00
}
}
2010-06-29 09:47:53 -04:00
2010-12-14 13:13:37 -05:00
_engine . main_thread ( ) - > get_buffers ( ) ;
2009-04-16 12:02:25 -04:00
2022-04-14 07:24:03 -04:00
boost : : shared_ptr < GraphChain > io_graph_chain = _io_graph_chain [ 0 ] ;
if ( io_graph_chain ) {
2022-05-16 20:25:23 -04:00
PortManager : : falloff_cache_calc ( nframes , _nominal_sample_rate ) ;
2022-04-14 07:24:03 -04:00
_process_graph - > process_io_plugs ( io_graph_chain , nframes , 0 ) ;
io_graph_chain . reset ( ) ; /* drop reference */
}
2008-06-02 17:41:35 -04:00
( this - > * process_function ) ( nframes ) ;
2009-10-14 12:10:01 -04:00
2022-04-14 07:24:03 -04:00
io_graph_chain = _io_graph_chain [ 1 ] ;
if ( io_graph_chain ) {
_process_graph - > process_io_plugs ( io_graph_chain , nframes , 0 ) ;
io_graph_chain . reset ( ) ; /* drop reference */
}
2015-04-28 16:18:30 -04:00
/* realtime-safe meter-position and processor-order changes
2015-04-26 00:00:08 -04:00
*
* ideally this would be done in
* Route : : process_output_buffers ( ) but various functions
2022-05-08 20:20:45 -04:00
* calling it hold a _processor_lock reader - lock
2015-04-26 00:00:08 -04:00
*/
2019-09-17 20:26:03 -04:00
bool one_or_more_routes_declicking = false ;
2020-04-18 17:54:24 -04:00
{
ProcessorChangeBlocker pcb ( this ) ;
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ( * i ) - > apply_processor_changes_rt ( ) ) {
_rt_emit_pending = true ;
}
if ( ( * i ) - > declick_in_progress ( ) ) {
one_or_more_routes_declicking = true ;
}
2019-09-17 20:26:03 -04:00
}
2015-04-28 16:18:30 -04:00
}
2019-09-17 20:26:03 -04:00
2022-02-06 22:30:48 -05:00
if ( _update_send_delaylines ) {
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
( * i ) - > update_send_delaylines ( ) ;
}
}
2015-04-28 16:18:30 -04:00
if ( _rt_emit_pending ) {
if ( ! _rt_thread_active ) {
emit_route_signals ( ) ;
}
if ( pthread_mutex_trylock ( & _rt_emit_mutex ) = = 0 ) {
pthread_cond_signal ( & _rt_emit_cond ) ;
pthread_mutex_unlock ( & _rt_emit_mutex ) ;
_rt_emit_pending = false ;
}
2015-04-26 00:00:08 -04:00
}
2019-09-17 20:26:03 -04:00
/* We are checking two things here:
*
* 1 ) whether or not all tracks have finished a declick out .
* 2 ) is the transport FSM waiting to be told this
*/
if ( ! one_or_more_routes_declicking & & declick_in_progress ( ) ) {
/* end of the declick has been reached by all routes */
2019-09-20 00:33:43 -04:00
TFSM_EVENT ( TransportFSM : : DeclickDone ) ;
2019-09-17 20:26:03 -04:00
}
2010-12-14 13:13:37 -05:00
_engine . main_thread ( ) - > drop_buffers ( ) ;
2010-04-13 16:48:33 -04:00
2017-09-18 12:39:17 -04:00
/* deliver MIDI clock. Note that we need to use the transport sample
2012-01-27 08:22:55 -05:00
* position at the start of process ( ) , not the value at the end of
* it . We may already have ticked ( ) because of a transport state
* change , for example .
*/
2012-01-05 00:05:31 -05:00
try {
2014-04-28 19:58:24 -04:00
_scene_changer - > run ( transport_at_start , transport_at_start + nframes ) ;
2012-01-05 00:05:31 -05:00
} catch ( . . . ) {
2012-01-06 15:29:18 -05:00
/* don't bother with a message */
2012-01-05 00:05:31 -05:00
}
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
SendFeedback ( ) ; /* EMIT SIGNAL */
}
int
2010-12-03 17:26:29 -05:00
Session : : fail_roll ( pframes_t nframes )
2009-04-23 13:48:37 -04:00
{
return no_roll ( nframes ) ;
}
int
2010-12-03 17:26:29 -05:00
Session : : no_roll ( pframes_t nframes )
2008-06-02 17:41:35 -04:00
{
2012-01-14 17:02:59 -05:00
PT_TIMING_CHECK ( 4 ) ;
2021-06-11 17:38:58 -04:00
TimerRAII tr ( dsp_stats [ NoRoll ] ) ;
2015-10-05 10:17:49 -04:00
2021-05-03 19:35:34 -04:00
samplepos_t end_sample = _transport_sample + floor ( nframes * _transport_fsm - > transport_speed ( ) ) ;
2008-06-02 17:41:35 -04:00
int ret = 0 ;
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
if ( _click_io ) {
2009-04-23 13:48:37 -04:00
_click_io - > silence ( nframes ) ;
2008-06-02 17:41:35 -04:00
}
2017-07-15 14:45:49 -04:00
VCAList v = _vca_manager - > vcas ( ) ;
for ( VCAList : : const_iterator i = v . begin ( ) ; i ! = v . end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
( * i ) - > automation_run ( _transport_sample , nframes ) ;
2017-07-15 14:45:49 -04:00
}
2019-09-17 20:26:03 -04:00
_global_locate_pending = locate_pending ( ) ;
2022-05-01 10:25:12 -04:00
boost : : shared_ptr < GraphChain > graph_chain = _graph_chain ;
if ( graph_chain ) {
2011-04-04 18:46:48 -04:00
DEBUG_TRACE ( DEBUG : : ProcessThreads , " calling graph/no-roll \n " ) ;
2022-05-01 10:25:12 -04:00
_process_graph - > routes_no_roll ( graph_chain , nframes , _transport_sample , end_sample , non_realtime_work_pending ( ) ) ;
2011-04-04 18:46:48 -04:00
} else {
2012-01-14 17:02:59 -05:00
PT_TIMING_CHECK ( 10 ) ;
2011-04-04 18:46:48 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2013-04-06 16:04:02 -04:00
if ( ( * i ) - > is_auditioner ( ) ) {
2011-04-04 18:46:48 -04:00
continue ;
}
2017-09-18 12:39:17 -04:00
if ( ( * i ) - > no_roll ( nframes , _transport_sample , end_sample , non_realtime_work_pending ( ) ) ) {
2011-04-04 18:46:48 -04:00
error < < string_compose ( _ ( " Session: error in no roll for %1 " ) , ( * i ) - > name ( ) ) < < endmsg ;
ret = - 1 ;
break ;
}
}
2012-01-14 17:02:59 -05:00
PT_TIMING_CHECK ( 11 ) ;
2011-04-04 18:46:48 -04:00
}
2008-06-02 17:41:35 -04:00
2012-01-14 17:02:59 -05:00
PT_TIMING_CHECK ( 5 ) ;
2008-06-02 17:41:35 -04:00
return ret ;
}
2012-01-30 15:58:17 -05:00
/** @param need_butler to be set to true by this method if it needs the butler,
2012-01-30 16:11:35 -05:00
* otherwise it must be left alone .
2012-01-30 15:58:17 -05:00
*/
2008-06-02 17:41:35 -04:00
int
2010-12-03 17:26:29 -05:00
Session : : process_routes ( pframes_t nframes , bool & need_butler )
2008-06-02 17:41:35 -04:00
{
2021-06-11 17:38:58 -04:00
TimerRAII tr ( dsp_stats [ Roll ] ) ;
2011-04-04 18:46:48 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2017-09-18 12:39:17 -04:00
const samplepos_t start_sample = _transport_sample ;
2021-05-03 19:35:34 -04:00
const samplepos_t end_sample = _transport_sample + floor ( nframes * _transport_fsm - > transport_speed ( ) ) ;
2015-10-05 10:17:49 -04:00
2021-02-05 16:18:21 -05:00
if ( actively_recording ( ) ) {
_capture_duration + = nframes ;
}
2017-07-15 14:45:49 -04:00
VCAList v = _vca_manager - > vcas ( ) ;
for ( VCAList : : const_iterator i = v . begin ( ) ; i ! = v . end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
( * i ) - > automation_run ( start_sample , nframes ) ;
2017-07-15 14:45:49 -04:00
}
2019-09-17 20:26:03 -04:00
_global_locate_pending = locate_pending ( ) ;
2017-07-05 17:24:02 -04:00
2022-05-01 10:25:12 -04:00
boost : : shared_ptr < GraphChain > graph_chain = _graph_chain ;
if ( graph_chain ) {
2011-04-04 18:46:48 -04:00
DEBUG_TRACE ( DEBUG : : ProcessThreads , " calling graph/process-routes \n " ) ;
2022-05-01 10:25:12 -04:00
if ( _process_graph - > process_routes ( graph_chain , nframes , start_sample , end_sample , need_butler ) < 0 ) {
2015-01-29 18:57:01 -05:00
stop_transport ( ) ;
return - 1 ;
}
2011-04-04 18:46:48 -04:00
} else {
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
int ret ;
2013-04-06 16:04:02 -04:00
if ( ( * i ) - > is_auditioner ( ) ) {
2011-04-04 18:46:48 -04:00
continue ;
}
2012-01-30 15:58:17 -05:00
bool b = false ;
2018-05-22 12:09:26 -04:00
if ( ( ret = ( * i ) - > roll ( nframes , start_sample , end_sample , b ) ) < 0 ) {
2021-11-10 17:55:58 -05:00
cerr < < " ERR1 STOP \n " ;
2019-09-20 00:33:43 -04:00
TFSM_STOP ( false , false ) ;
2011-04-04 18:46:48 -04:00
return - 1 ;
}
2012-01-30 15:58:17 -05:00
if ( b ) {
2017-04-11 12:38:34 -04:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1 rolled and needs butler \n " , ( * i ) - > name ( ) ) ) ;
2012-01-30 15:58:17 -05:00
need_butler = true ;
}
2011-04-04 18:46:48 -04:00
}
}
2008-06-02 17:41:35 -04:00
return 0 ;
}
void
2010-04-21 16:42:22 -04:00
Session : : get_track_statistics ( )
2008-06-02 17:41:35 -04:00
{
float pworst = 1.0f ;
float cworst = 1.0f ;
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 ) {
2008-06-02 17:41:35 -04:00
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
2017-07-25 11:26:14 -04:00
if ( ! tr | | tr - > is_private_route ( ) ) {
2008-06-02 17:41:35 -04:00
continue ;
}
2009-10-14 12:10:01 -04:00
2010-04-21 16:42:22 -04:00
pworst = min ( pworst , tr - > playback_buffer_load ( ) ) ;
cworst = min ( cworst , tr - > capture_buffer_load ( ) ) ;
2008-06-02 17:41:35 -04:00
}
g_atomic_int_set ( & _playback_load , ( uint32_t ) floor ( pworst * 100.0f ) ) ;
g_atomic_int_set ( & _capture_load , ( uint32_t ) floor ( cworst * 100.0f ) ) ;
if ( actively_recording ( ) ) {
set_dirty ( ) ;
}
}
2018-09-18 18:51:59 -04:00
bool
Session : : compute_audible_delta ( samplepos_t & pos_and_delta ) const
{
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) = = 0.0 | | _count_in_samples > 0 | | _remaining_latency_preroll > 0 ) {
2018-09-18 18:51:59 -04:00
/* cannot compute audible delta, because the session is
generating silence that does not correspond to the timeline ,
but is instead filling playback buffers to manage latency
alignment .
*/
2021-05-03 19:35:34 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " still adjusting for latency (%1) and/or count-in (%2) or stopped %1 \n " , _remaining_latency_preroll , _count_in_samples , _transport_fsm - > transport_speed ( ) ) ) ;
2018-09-18 18:51:59 -04:00
return false ;
}
pos_and_delta - = _transport_sample ;
return true ;
}
2020-03-05 14:47:19 -05:00
samplecnt_t
Session : : calc_preroll_subcycle ( samplecnt_t ns ) const
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
samplecnt_t route_offset = ( * i ) - > playback_latency ( ) ;
if ( _remaining_latency_preroll > route_offset + ns ) {
/* route will no-roll for complete pre-roll cycle */
continue ;
}
if ( _remaining_latency_preroll > route_offset ) {
/* route may need partial no-roll and partial roll from
* ( _transport_sample - _remaining_latency_preroll ) . . + ns .
* shorten and split the cycle .
*/
ns = std : : min ( ns , ( _remaining_latency_preroll - route_offset ) ) ;
}
}
return ns ;
}
2008-06-02 17:41:35 -04:00
/** Process callback used when the auditioner is not active */
void
2010-12-03 17:26:29 -05:00
Session : : process_with_events ( pframes_t nframes )
2008-06-02 17:41:35 -04:00
{
2012-01-14 17:02:59 -05:00
PT_TIMING_CHECK ( 3 ) ;
2021-06-11 17:38:58 -04:00
TimerRAII tr ( dsp_stats [ ProcessFunction ] ) ;
2015-10-05 10:17:49 -04:00
2010-09-17 12:24:22 -04:00
SessionEvent * ev ;
2010-12-03 17:26:29 -05:00
pframes_t this_nframes ;
2017-09-18 12:39:17 -04:00
samplepos_t end_sample ;
2009-01-10 03:42:07 -05:00
bool session_needs_butler = false ;
2017-09-18 12:39:17 -04:00
samplecnt_t samples_moved ;
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
/* make sure the auditioner is silent */
if ( auditioner ) {
2009-04-23 13:48:37 -04:00
auditioner - > silence ( nframes ) ;
2008-06-02 17:41:35 -04:00
}
/* handle any pending events */
while ( pending_events . read ( & ev , 1 ) = = 1 ) {
merge_event ( ev ) ;
}
/* if we are not in the middle of a state change,
and there are immediate events queued up ,
2009-10-14 12:10:01 -04:00
process them .
2008-06-02 17:41:35 -04:00
*/
while ( ! non_realtime_work_pending ( ) & & ! immediate_events . empty ( ) ) {
2009-12-03 21:15:12 -05:00
SessionEvent * ev = immediate_events . front ( ) ;
2008-06-02 17:41:35 -04:00
immediate_events . pop_front ( ) ;
process_event ( ev ) ;
}
2022-06-10 13:59:12 -04:00
2017-09-28 00:31:12 -04:00
/* only count-in when going to roll at speed 1.0 */
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) ! = 1.0 & & _count_in_samples > 0 ) {
2017-01-17 14:43:55 -05:00
_count_in_samples = 0 ;
}
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) = = 0.0 ) {
2017-09-28 00:31:12 -04:00
_remaining_latency_preroll = 0 ;
}
assert ( _count_in_samples = = 0 | | _remaining_latency_preroll = = 0 | | _count_in_samples = = _remaining_latency_preroll ) ;
2017-01-17 14:43:55 -05:00
2019-12-29 20:41:46 -05:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("Running count in/latency preroll of %1 & %2\n", _count_in_samples, _remaining_latency_preroll));
2018-09-18 18:51:59 -04:00
2022-06-10 13:59:12 -04:00
TriggerBox : : begin_process_cycle ( ) ;
2022-01-06 02:23:27 -05:00
maybe_find_pending_cue ( ) ;
2021-11-01 14:09:29 -04:00
2017-10-11 19:49:05 -04:00
while ( _count_in_samples > 0 | | _remaining_latency_preroll > 0 ) {
2017-09-28 00:31:12 -04:00
samplecnt_t ns ;
if ( _remaining_latency_preroll > 0 ) {
ns = std : : min ( ( samplecnt_t ) nframes , _remaining_latency_preroll ) ;
} else {
ns = std : : min ( ( samplecnt_t ) nframes , _count_in_samples ) ;
}
2020-03-05 14:47:19 -05:00
/* process until next route in-point */
ns = calc_preroll_subcycle ( ns ) ;
2017-09-30 07:10:17 -04:00
2017-09-28 00:31:12 -04:00
if ( _count_in_samples > 0 ) {
run_click ( _transport_sample - _count_in_samples , ns ) ;
assert ( _count_in_samples > = ns ) ;
_count_in_samples - = ns ;
}
2017-01-17 14:43:55 -05:00
2017-09-28 00:31:12 -04:00
if ( _remaining_latency_preroll > 0 ) {
if ( _count_in_samples = = 0 ) {
click ( _transport_sample - _remaining_latency_preroll , ns ) ;
}
if ( process_routes ( ns , session_needs_butler ) ) {
fail_roll ( ns ) ;
}
} else {
no_roll ( ns ) ;
}
if ( _remaining_latency_preroll > 0 ) {
assert ( _remaining_latency_preroll > = ns ) ;
_remaining_latency_preroll - = ns ;
}
2017-01-17 14:43:55 -05:00
nframes - = ns ;
/* process events.. */
if ( ! events . empty ( ) & & next_event ! = events . end ( ) ) {
SessionEvent * this_event = * next_event ;
Events : : iterator the_next_one = next_event ;
+ + the_next_one ;
2017-09-18 12:39:17 -04:00
while ( this_event & & this_event - > action_sample = = _transport_sample ) {
2017-01-17 14:43:55 -05:00
process_event ( this_event ) ;
if ( the_next_one = = events . end ( ) ) {
this_event = 0 ;
} else {
this_event = * the_next_one ;
+ + the_next_one ;
}
}
set_next_event ( ) ;
}
if ( nframes = = 0 ) {
return ;
} else {
_engine . split_cycle ( ns ) ;
}
}
2011-01-03 18:55:00 -05:00
/* Decide on what to do with quarter-frame MTC during this cycle */
bool const was_sending_qf_mtc = _send_qf_mtc ;
double const tolerance = Config - > get_mtc_qf_speed_tolerance ( ) / 100.0 ;
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) ! = 0 ) {
2011-01-03 22:35:10 -05:00
_send_qf_mtc = (
Config - > get_send_mtc ( ) & &
2021-05-03 19:35:34 -04:00
_transport_fsm - > transport_speed ( ) > = ( 1 - tolerance ) & &
_transport_fsm - > transport_speed ( ) < = ( 1 + tolerance )
2011-01-03 22:35:10 -05:00
) ;
2011-04-04 18:46:48 -04:00
2011-01-03 22:35:10 -05:00
if ( _send_qf_mtc & & ! was_sending_qf_mtc ) {
/* we will re-start quarter-frame MTC this cycle, so send a full update to set things up */
_send_timecode_update = true ;
}
2011-04-04 18:46:48 -04:00
2017-09-18 12:39:17 -04:00
if ( Config - > get_send_mtc ( ) & & ! _send_qf_mtc & & _pframes_since_last_mtc > ( sample_rate ( ) / 4 ) ) {
2011-01-03 22:35:10 -05:00
/* we're sending MTC, but we're not sending QF MTC at the moment, and it's been
a quarter of a second since we sent anything at all , so send a full MTC update
this cycle .
*/
_send_timecode_update = true ;
}
2011-04-04 18:46:48 -04:00
2011-01-03 22:35:10 -05:00
_pframes_since_last_mtc + = nframes ;
2011-01-03 18:55:00 -05:00
}
2011-04-04 18:46:48 -04:00
2011-01-03 18:55:00 -05:00
/* Events caused a transport change (or we re-started sending
* MTC ) , so send an MTC Full Frame ( Timecode ) message . This
* is sent whether rolling or not , to give slaves an idea of
* ardour time on locates ( and allow slow slaves to position
* and prepare for rolling )
2008-06-02 17:41:35 -04:00
*/
2009-10-26 10:38:58 -04:00
if ( _send_timecode_update ) {
2017-09-18 12:39:17 -04:00
send_full_time_code ( _transport_sample , nframes ) ;
2008-06-02 17:41:35 -04:00
}
if ( ! process_can_proceed ( ) ) {
_silent = true ;
return ;
}
if ( events . empty ( ) | | next_event = = events . end ( ) ) {
2016-02-23 09:41:21 -05:00
try_run_lua ( nframes ) ; // also during export ?? ->move to process_without_events()
2017-01-02 11:03:33 -05:00
/* lua scripts may inject events */
while ( _n_lua_scripts > 0 & & pending_events . read ( & ev , 1 ) = = 1 ) {
merge_event ( ev ) ;
}
if ( events . empty ( ) | | next_event = = events . end ( ) ) {
process_without_events ( nframes ) ;
return ;
}
2008-06-02 17:41:35 -04:00
}
2021-05-03 19:35:34 -04:00
assert ( _transport_fsm - > transport_speed ( ) = = 0 | | _transport_fsm - > transport_speed ( ) = = 1.0 | | _transport_fsm - > transport_speed ( ) = = - 1.0 ) ;
2017-10-31 17:01:46 -04:00
2021-05-03 19:35:34 -04:00
samples_moved = ( samplecnt_t ) nframes * _transport_fsm - > transport_speed ( ) ;
// DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed()));
2009-01-10 03:42:07 -05:00
2017-09-18 12:39:17 -04:00
end_sample = _transport_sample + samples_moved ;
2008-06-02 17:41:35 -04:00
{
2009-12-03 21:15:12 -05:00
SessionEvent * this_event ;
2008-06-02 17:41:35 -04:00
Events : : iterator the_next_one ;
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
if ( ! process_can_proceed ( ) ) {
_silent = true ;
return ;
}
2009-10-14 12:10:01 -04:00
2018-09-18 18:51:59 -04:00
if ( ! _exporting & & config . get_external_sync ( ) ) {
2020-03-20 19:38:23 -04:00
if ( ! implement_master_strategy ( ) ) {
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
}
2009-10-14 12:10:01 -04:00
}
2008-06-02 17:41:35 -04:00
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) = = 0 ) {
2009-04-23 13:48:37 -04:00
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
}
2009-10-14 12:10:01 -04:00
2015-04-23 22:39:57 -04:00
2017-09-18 12:39:17 -04:00
samplepos_t stop_limit = compute_stop_limit ( ) ;
2008-06-02 17:41:35 -04:00
if ( maybe_stop ( stop_limit ) ) {
2019-11-03 08:42:10 -05:00
if ( ! _exporting & & ! timecode_transmission_suspended ( ) ) {
send_midi_time_code_for_cycle ( _transport_sample , end_sample , nframes ) ;
}
2009-04-23 13:48:37 -04:00
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
2009-10-14 12:10:01 -04:00
}
2008-06-02 17:41:35 -04:00
this_event = * next_event ;
the_next_one = next_event ;
+ + the_next_one ;
/* yes folks, here it is, the actual loop where we really truly
2009-10-14 12:10:01 -04:00
process some audio
2009-04-23 13:48:37 -04:00
*/
2008-06-02 17:41:35 -04:00
while ( nframes ) {
this_nframes = nframes ; /* real (jack) time relative */
2021-05-03 19:35:34 -04:00
samples_moved = ( samplecnt_t ) floor ( _transport_fsm - > transport_speed ( ) * nframes ) ; /* transport relative */
// DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed()));
2008-06-02 17:41:35 -04:00
/* running an event, position transport precisely to its time */
2017-09-18 12:39:17 -04:00
if ( this_event & & this_event - > action_sample < = end_sample & & this_event - > action_sample > = _transport_sample ) {
2008-06-02 17:41:35 -04:00
/* this isn't quite right for reverse play */
2017-09-18 12:39:17 -04:00
samples_moved = ( samplecnt_t ) ( this_event - > action_sample - _transport_sample ) ;
2021-05-03 19:35:34 -04:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop2 (for %4)plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed(), enum_2_string (this_event->type)));
this_nframes = abs ( floor ( samples_moved / _transport_fsm - > transport_speed ( ) ) ) ;
2009-10-14 12:10:01 -04:00
}
2008-06-02 17:41:35 -04:00
2016-02-23 09:41:21 -05:00
try_run_lua ( this_nframes ) ;
2008-06-02 17:41:35 -04:00
if ( this_nframes ) {
2009-10-14 12:10:01 -04:00
2019-11-03 08:42:10 -05:00
if ( ! _exporting & & ! timecode_transmission_suspended ( ) ) {
send_midi_time_code_for_cycle ( _transport_sample , _transport_sample + samples_moved , this_nframes ) ;
}
2017-09-18 12:39:17 -04:00
click ( _transport_sample , this_nframes ) ;
2009-10-14 12:10:01 -04:00
2010-03-27 10:41:24 -04:00
if ( process_routes ( this_nframes , session_needs_butler ) ) {
2009-04-23 13:48:37 -04:00
fail_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
}
2011-04-04 18:46:48 -04:00
2010-05-11 19:22:15 -04:00
get_track_statistics ( ) ;
2008-06-02 17:41:35 -04:00
nframes - = this_nframes ;
2009-10-14 12:10:01 -04:00
2017-09-18 12:39:17 -04:00
if ( samples_moved < 0 ) {
decrement_transport_position ( - samples_moved ) ;
} else if ( samples_moved ) {
increment_transport_position ( samples_moved ) ;
2019-03-03 15:17:42 -05:00
} else {
DEBUG_TRACE ( DEBUG : : Transport , " no transport motion \n " ) ;
2008-06-02 17:41:35 -04:00
}
maybe_stop ( stop_limit ) ;
}
2014-01-03 12:36:00 -05:00
if ( nframes > 0 ) {
_engine . split_cycle ( this_nframes ) ;
}
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
/* now handle this event and all others scheduled for the same time */
2009-10-14 12:10:01 -04:00
2017-09-18 12:39:17 -04:00
while ( this_event & & this_event - > action_sample = = _transport_sample ) {
2008-06-02 17:41:35 -04:00
process_event ( this_event ) ;
if ( the_next_one = = events . end ( ) ) {
this_event = 0 ;
} else {
this_event = * the_next_one ;
+ + the_next_one ;
}
2009-10-14 12:10:01 -04:00
}
2008-06-02 17:41:35 -04:00
/* if an event left our state changing, do the right thing */
2009-04-16 12:02:25 -04:00
if ( nframes & & non_realtime_work_pending ( ) ) {
2009-04-23 13:48:37 -04:00
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
break ;
}
/* this is necessary to handle the case of seamless looping */
2021-05-03 19:35:34 -04:00
end_sample = _transport_sample + floor ( nframes * _transport_fsm - > transport_speed ( ) ) ;
2008-06-02 17:41:35 -04:00
}
set_next_event ( ) ;
} /* implicit release of route lock */
2022-01-06 02:23:27 -05:00
clear_active_cue ( ) ;
2021-11-01 14:09:29 -04:00
2009-10-23 20:39:28 -04:00
if ( session_needs_butler ) {
2017-04-11 12:38:34 -04:00
DEBUG_TRACE ( DEBUG : : Butler , " p-with-events: session needs butler, call it \n " ) ;
2009-10-23 20:39:28 -04:00
_butler - > summon ( ) ;
}
2008-06-02 17:41:35 -04:00
}
bool
Session : : transport_locked ( ) const
{
2018-09-18 18:51:59 -04:00
if ( ! locate_pending ( ) & & ( ! config . get_external_sync ( ) | | ( transport_master ( ) - > ok ( ) & & transport_master ( ) - > locked ( ) ) ) ) {
2009-01-01 16:26:23 -05:00
return true ;
}
2008-06-02 17:41:35 -04:00
2009-01-01 16:26:23 -05:00
return false ;
}
2008-06-02 17:41:35 -04:00
void
2010-12-03 17:26:29 -05:00
Session : : process_without_events ( pframes_t nframes )
2008-06-02 17:41:35 -04:00
{
2021-06-11 17:38:58 -04:00
TimerRAII tr ( dsp_stats [ ProcessFunction ] ) ;
2008-06-02 17:41:35 -04:00
bool session_needs_butler = false ;
2017-09-18 12:39:17 -04:00
samplecnt_t samples_moved ;
2008-06-02 17:41:35 -04:00
if ( ! process_can_proceed ( ) ) {
_silent = true ;
return ;
}
2018-09-18 18:51:59 -04:00
if ( ! _exporting & & config . get_external_sync ( ) ) {
2020-03-20 19:38:23 -04:00
if ( ! implement_master_strategy ( ) ) {
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
}
2009-10-14 12:10:01 -04:00
}
2008-06-02 17:41:35 -04:00
2021-05-03 19:35:34 -04:00
assert ( _transport_fsm - > transport_speed ( ) = = 0 | | _transport_fsm - > transport_speed ( ) = = 1.0 | | _transport_fsm - > transport_speed ( ) = = - 1.0 ) ;
2018-09-18 18:51:59 -04:00
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) = = 0 ) {
2020-02-21 13:53:53 -05:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("transport not moving @ %1\n", _transport_sample));
2017-06-27 15:33:27 -04:00
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
2018-09-18 18:51:59 -04:00
} else {
2021-05-03 19:35:34 -04:00
samples_moved = ( samplecnt_t ) nframes * _transport_fsm - > transport_speed ( ) ;
// DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_fsm->transport_speed()));
2008-06-02 17:41:35 -04:00
}
2009-10-14 12:10:01 -04:00
2010-08-01 21:59:34 -04:00
if ( ! _exporting & & ! timecode_transmission_suspended ( ) ) {
2017-09-18 12:39:17 -04:00
send_midi_time_code_for_cycle ( _transport_sample , _transport_sample + samples_moved , nframes ) ;
2008-06-02 17:41:35 -04:00
}
2017-09-18 12:39:17 -04:00
samplepos_t const stop_limit = compute_stop_limit ( ) ;
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
if ( maybe_stop ( stop_limit ) ) {
2017-06-27 15:33:27 -04:00
no_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
2009-10-14 12:10:01 -04:00
}
2008-06-02 17:41:35 -04:00
2009-04-23 13:48:37 -04:00
if ( maybe_sync_start ( nframes ) ) {
2008-06-02 17:41:35 -04:00
return ;
}
2017-09-18 12:39:17 -04:00
click ( _transport_sample , nframes ) ;
2008-06-02 17:41:35 -04:00
2022-06-10 13:59:12 -04:00
TriggerBox : : begin_process_cycle ( ) ;
2022-01-06 02:23:27 -05:00
maybe_find_pending_cue ( ) ;
2021-11-01 14:09:29 -04:00
2010-03-27 10:41:24 -04:00
if ( process_routes ( nframes , session_needs_butler ) ) {
2009-04-23 13:48:37 -04:00
fail_roll ( nframes ) ;
2008-06-02 17:41:35 -04:00
return ;
}
2022-01-06 02:23:27 -05:00
clear_active_cue ( ) ;
2021-11-01 14:09:29 -04:00
2010-05-11 19:22:15 -04:00
get_track_statistics ( ) ;
2017-09-18 12:39:17 -04:00
if ( samples_moved < 0 ) {
decrement_transport_position ( - samples_moved ) ;
2021-05-03 19:35:34 -04:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("DEcrement transport by %1 to %2\n", samples_moved, _transport_sample));
2017-09-18 12:39:17 -04:00
} else if ( samples_moved ) {
increment_transport_position ( samples_moved ) ;
2021-05-03 19:35:34 -04:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample));
2019-03-03 15:17:42 -05:00
} else {
DEBUG_TRACE ( DEBUG : : Transport , " no transport motion \n " ) ;
2008-06-02 17:41:35 -04:00
}
maybe_stop ( stop_limit ) ;
2009-10-23 20:39:28 -04:00
if ( session_needs_butler ) {
2017-04-11 12:38:34 -04:00
DEBUG_TRACE ( DEBUG : : Butler , " p-without-events: session needs butler, call it \n " ) ;
2009-10-23 20:39:28 -04:00
_butler - > summon ( ) ;
}
2008-06-02 17:41:35 -04:00
}
/** Process callback used when the auditioner is active.
2017-09-18 12:39:17 -04:00
* @ param nframes number of samples to process .
2008-06-02 17:41:35 -04:00
*/
void
2010-12-03 17:26:29 -05:00
Session : : process_audition ( pframes_t nframes )
2008-06-02 17:41:35 -04:00
{
2009-12-03 21:15:12 -05:00
SessionEvent * ev ;
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2022-06-03 16:49:56 -04:00
boost : : shared_ptr < GraphChain > graph_chain = _graph_chain ;
if ( graph_chain ) {
2022-06-04 10:24:49 -04:00
/* Ideally we'd use Session::rt_tasklist, since dependency is irrelevant. */
2022-06-03 16:49:56 -04:00
_process_graph - > silence_routes ( graph_chain , nframes ) ;
} else {
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
if ( ! ( * i ) - > is_auditioner ( ) ) {
( * i ) - > silence ( nframes ) ;
}
2008-06-02 17:41:35 -04:00
}
}
/* handle pending events */
while ( pending_events . read ( & ev , 1 ) = = 1 ) {
merge_event ( ev ) ;
}
/* if we are not in the middle of a state change,
and there are immediate events queued up ,
2009-10-14 12:10:01 -04:00
process them .
2008-06-02 17:41:35 -04:00
*/
while ( ! non_realtime_work_pending ( ) & & ! immediate_events . empty ( ) ) {
2009-12-03 21:15:12 -05:00
SessionEvent * ev = immediate_events . front ( ) ;
2008-06-02 17:41:35 -04:00
immediate_events . pop_front ( ) ;
process_event ( ev ) ;
}
2022-02-05 13:16:00 -05:00
/* run the auditioner, and if it says we need butler service, ask for it */
if ( auditioner - > play_audition ( nframes ) > 0 ) {
DEBUG_TRACE ( DEBUG : : Butler , " auditioner needs butler, call it \n " ) ;
_butler - > summon ( ) ;
}
/* if using a monitor section, run it because otherwise we don't hear anything */
if ( _monitor_out & & auditioner - > needs_monitor ( ) ) {
_monitor_out - > monitor_run ( _transport_sample , _transport_sample + nframes , nframes ) ;
}
2010-03-24 23:40:07 -04:00
if ( ! auditioner - > auditioning ( ) ) {
2008-06-02 17:41:35 -04:00
/* auditioner no longer active, so go back to the normal process callback */
process_function = & Session : : process_with_events ;
}
}
bool
2010-12-03 17:26:29 -05:00
Session : : maybe_sync_start ( pframes_t & nframes )
2008-06-02 17:41:35 -04:00
{
2010-12-03 17:26:29 -05:00
pframes_t sync_offset ;
2008-06-02 17:41:35 -04:00
if ( ! waiting_for_sync_offset ) {
return false ;
}
if ( _engine . get_sync_offset ( sync_offset ) & & sync_offset < nframes ) {
2008-09-10 11:03:30 -04:00
/* generate silence up to the sync point, then
adjust nframes + offset to reflect whatever
is left to do .
*/
2009-04-23 13:48:37 -04:00
no_roll ( sync_offset ) ;
2008-06-02 17:41:35 -04:00
nframes - = sync_offset ;
2011-03-10 21:55:52 -05:00
Port : : increment_global_port_buffer_offset ( sync_offset ) ;
2008-06-02 17:41:35 -04:00
waiting_for_sync_offset = false ;
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
if ( nframes = = 0 ) {
2008-09-10 11:03:30 -04:00
return true ; // done, nothing left to process
2008-06-02 17:41:35 -04:00
}
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
} else {
2008-09-10 11:03:30 -04:00
/* sync offset point is not within this process()
2009-10-14 12:10:01 -04:00
cycle , so just generate silence . and don ' t bother
2008-09-10 11:03:30 -04:00
with any fancy stuff here , just the minimal silence .
*/
2009-04-16 12:02:25 -04:00
_silent = true ;
2008-09-10 11:03:30 -04:00
if ( Config - > get_locate_while_waiting_for_sync ( ) ) {
2019-02-26 11:02:28 -05:00
DEBUG_TRACE ( DEBUG : : Transport , " micro-locate while waiting for sync \n " ) ;
2008-09-10 11:03:30 -04:00
if ( micro_locate ( nframes ) ) {
/* XXX ERROR !!! XXX */
}
}
return true ; // done, nothing left to process
2008-06-02 17:41:35 -04:00
}
return false ;
}
2009-12-03 21:15:12 -05:00
void
Session : : queue_event ( SessionEvent * ev )
{
2019-03-18 10:33:05 -04:00
if ( deletion_in_progress ( ) ) {
2009-12-03 21:15:12 -05:00
return ;
2019-03-18 10:33:05 -04:00
} else if ( loading ( ) ) {
2009-12-03 21:15:12 -05:00
merge_event ( ev ) ;
} else {
2017-07-04 14:44:33 -04:00
Glib : : Threads : : Mutex : : Lock lm ( rb_write_lock ) ;
2009-12-03 21:15:12 -05:00
pending_events . write ( & ev , 1 ) ;
}
}
void
Session : : set_next_event ( )
{
if ( events . empty ( ) ) {
next_event = events . end ( ) ;
return ;
}
if ( next_event = = events . end ( ) ) {
next_event = events . begin ( ) ;
}
2017-09-18 12:39:17 -04:00
if ( ( * next_event ) - > action_sample > _transport_sample ) {
2009-12-03 21:15:12 -05:00
next_event = events . begin ( ) ;
}
for ( ; next_event ! = events . end ( ) ; + + next_event ) {
2017-09-18 12:39:17 -04:00
if ( ( * next_event ) - > action_sample > = _transport_sample ) {
2009-12-03 21:15:12 -05:00
break ;
}
}
2022-01-05 12:26:10 -05:00
if ( next_event ! = events . end ( ) ) {
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " @ %1 next event set to %2 @ %3 \n " , _transport_sample , enum_2_string ( ( * next_event ) - > type ) , ( * next_event ) - > action_sample ) ) ;
} else {
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " no next event for %1 \n " , _transport_sample ) ) ;
}
2009-12-03 21:15:12 -05:00
}
void
Session : : process_event ( SessionEvent * ev )
{
bool remove = true ;
bool del = true ;
/* if we're in the middle of a state change (i.e. waiting
for the butler thread to complete the non - realtime
part of the change ) , we ' ll just have to queue this
event for a time when the change is complete .
*/
if ( non_realtime_work_pending ( ) ) {
/* except locates, which we have the capability to handle */
if ( ev - > type ! = SessionEvent : : Locate ) {
immediate_events . insert ( immediate_events . end ( ) , ev ) ;
_remove_event ( ev ) ;
return ;
}
}
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " Processing event: %1 @ %2 \n " , enum_2_string ( ev - > type ) , _transport_sample ) ) ;
2009-12-03 21:15:12 -05:00
switch ( ev - > type ) {
case SessionEvent : : SetLoop :
2020-05-16 14:24:54 -04:00
/* this is the event sent by a UI to define whether or not we
use loop range playback or not .
*/
2019-12-05 15:00:24 -05:00
set_play_loop ( ev - > yes_or_no , true ) ;
2009-12-03 21:15:12 -05:00
break ;
case SessionEvent : : AutoLoop :
2020-05-16 14:27:45 -04:00
/* this is the event created by the Session that marks
2020-05-16 14:24:54 -04:00
the end of the loop range and if we ' re loop playing ,
triggers a special kind of locate back to the start of the
loop range .
*/
2009-12-03 21:15:12 -05:00
if ( play_loop ) {
2021-04-28 21:54:51 -04:00
/* roll after locate, set "for loop end" true
2012-06-22 10:27:51 -04:00
*/
2021-04-28 21:54:51 -04:00
TFSM_LOCATE ( ev - > target_sample , MustRoll , true , false ) ;
2009-12-03 21:15:12 -05:00
}
remove = false ;
del = false ;
break ;
case SessionEvent : : Locate :
2019-11-23 17:54:09 -05:00
/* args: do not roll after locate, clear state, not for loop, force */
2020-03-24 20:22:11 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " sending locate to %1 to tfsm \n " , ev - > target_sample ) ) ;
2021-04-28 21:54:51 -04:00
TFSM_LOCATE ( ev - > target_sample , ev - > locate_transport_disposition , false , ev - > yes_or_no ) ;
2009-12-03 21:15:12 -05:00
_send_timecode_update = true ;
break ;
case SessionEvent : : LocateRoll :
2019-11-23 17:53:54 -05:00
/* args: roll after locate, clear state if not looping, not for loop, force */
2021-04-28 21:54:51 -04:00
TFSM_LOCATE ( ev - > target_sample , MustRoll , false , ev - > yes_or_no ) ;
2009-12-03 21:15:12 -05:00
_send_timecode_update = true ;
break ;
2014-09-17 10:31:33 -04:00
case SessionEvent : : Skip :
2014-09-17 12:27:21 -04:00
if ( Config - > get_skip_playback ( ) ) {
2021-04-28 21:54:51 -04:00
TFSM_LOCATE ( ev - > target_sample , MustRoll , false , false ) ;
2014-09-17 12:27:21 -04:00
_send_timecode_update = true ;
}
2014-09-17 10:31:33 -04:00
remove = false ;
del = false ;
break ;
2009-12-03 21:15:12 -05:00
case SessionEvent : : LocateRollLocate :
// locate is handled by ::request_roll_at_and_return()
2017-09-18 12:39:17 -04:00
_requested_return_sample = ev - > target_sample ;
2021-04-28 21:54:51 -04:00
TFSM_LOCATE ( ev - > target2_sample , MustRoll , false , false ) ;
2019-11-02 18:11:56 -04:00
_send_timecode_update = true ;
2009-12-03 21:15:12 -05:00
break ;
2020-03-13 15:50:34 -04:00
case SessionEvent : : SetTransportSpeed :
2021-06-18 13:21:15 -04:00
TFSM_SPEED ( ev - > speed ) ;
2009-12-03 21:15:12 -05:00
break ;
2021-07-18 21:48:13 -04:00
case SessionEvent : : SetDefaultPlaySpeed :
2021-07-18 21:40:09 -04:00
set_default_play_speed ( ev - > speed ) ;
break ;
2021-04-16 01:09:41 -04:00
case SessionEvent : : StartRoll :
TFSM_ROLL ( ) ;
break ;
case SessionEvent : : EndRoll :
2021-12-22 19:27:07 -05:00
TFSM_STOP ( ev - > yes_or_no , ev - > second_yes_or_no ) ;
2021-04-16 01:09:41 -04:00
break ;
2018-09-18 18:51:59 -04:00
case SessionEvent : : SetTransportMaster :
2020-05-15 23:06:11 -04:00
/* do not allow changing the transport master if we're already
using one .
*/
if ( ! config . get_external_sync ( ) ) {
TransportMasterManager : : instance ( ) . set_current ( ev - > transport_master ) ;
}
2018-09-18 18:51:59 -04:00
break ;
2009-12-03 21:15:12 -05:00
case SessionEvent : : PunchIn :
2017-09-18 12:39:17 -04:00
// cerr << "PunchIN at " << transport_sample() << endl;
2017-09-29 15:01:50 -04:00
if ( config . get_punch_in ( ) & & record_status ( ) = = Enabled ) {
2009-12-03 21:15:12 -05:00
enable_record ( ) ;
}
remove = false ;
del = false ;
break ;
case SessionEvent : : PunchOut :
2017-09-18 12:39:17 -04:00
// cerr << "PunchOUT at " << transport_sample() << endl;
2017-09-29 15:01:50 -04:00
if ( config . get_punch_out ( ) ) {
2009-12-03 21:15:12 -05:00
step_back_from_record ( ) ;
}
remove = false ;
del = false ;
break ;
case SessionEvent : : RangeStop :
2021-11-10 17:55:58 -05:00
cerr < < " RANGE STOP \n " ;
2019-09-20 00:33:43 -04:00
TFSM_STOP ( ev - > yes_or_no , false ) ;
2009-12-03 21:15:12 -05:00
remove = false ;
del = false ;
break ;
case SessionEvent : : RangeLocate :
2021-04-28 21:54:51 -04:00
/* args: roll after locate, not with loop */
TFSM_LOCATE ( ev - > target_sample , MustRoll , false , false ) ;
2009-12-03 21:15:12 -05:00
remove = false ;
del = false ;
break ;
case SessionEvent : : Overwrite :
2020-03-28 12:31:12 -04:00
if ( boost : : shared_ptr < Track > track = ev - > track . lock ( ) ) {
overwrite_some_buffers ( track , ev - > overwrite ) ;
}
2009-12-03 21:15:12 -05:00
break ;
2020-04-12 07:35:21 -04:00
case SessionEvent : : OverwriteAll :
overwrite_some_buffers ( boost : : shared_ptr < Track > ( ) , ev - > overwrite ) ;
break ;
2021-06-30 21:12:27 -04:00
case SessionEvent : : TransportStateChange :
TransportStateChange ( ) ; /* EMIT SIGNAL */
break ;
2009-12-03 21:15:12 -05:00
case SessionEvent : : Audition :
set_audition ( ev - > region ) ;
// drop reference to region
ev - > region . reset ( ) ;
break ;
case SessionEvent : : SetPlayAudioRange :
2021-06-22 14:03:32 -04:00
set_play_range ( ev - > audio_range , ( ev - > speed = = _transport_fsm - > default_speed ( ) ) ) ; //an explicit PLAY state would be nicer here
2009-12-03 21:15:12 -05:00
break ;
2014-07-02 18:34:49 -04:00
case SessionEvent : : CancelPlayAudioRange :
unset_play_range ( ) ;
break ;
2009-12-07 16:37:35 -05:00
case SessionEvent : : RealTimeOperation :
process_rtop ( ev ) ;
del = false ; // other side of RT request needs to clean up
break ;
2011-04-04 18:46:48 -04:00
case SessionEvent : : AdjustPlaybackBuffering :
schedule_playback_buffering_adjustment ( ) ;
break ;
2010-06-09 13:24:07 -04:00
2011-04-04 18:46:48 -04:00
case SessionEvent : : AdjustCaptureBuffering :
schedule_capture_buffering_adjustment ( ) ;
break ;
2010-06-09 13:24:07 -04:00
2010-08-01 21:59:34 -04:00
case SessionEvent : : SetTimecodeTransmission :
g_atomic_int_set ( & _suspend_timecode_transmission , ev - > yes_or_no ? 0 : 1 ) ;
break ;
2022-01-04 20:03:28 -05:00
case SessionEvent : : SyncCues :
sync_cues ( ) ;
break ;
2009-12-03 21:15:12 -05:00
default :
fatal < < string_compose ( _ ( " Programming error: illegal event type in process_event (%1) " ) , ev - > type ) < < endmsg ;
2014-11-14 04:47:43 -05:00
abort ( ) ; /*NOTREACHED*/
2009-12-03 21:15:12 -05:00
break ;
} ;
if ( remove ) {
2021-07-26 19:27:28 -04:00
del = ( del & & ! _remove_event ( ev ) ) ;
2009-12-03 21:15:12 -05:00
}
if ( del ) {
delete ev ;
}
}
2009-12-07 16:37:35 -05:00
2022-03-14 14:18:20 -04:00
void
Session : : handle_slots_empty_status ( boost : : weak_ptr < Route > const & wr )
{
boost : : shared_ptr < Route > r = wr . lock ( ) ;
if ( ! r ) {
return ;
}
if ( r - > triggerbox ( ) ) {
if ( r - > triggerbox ( ) - > empty ( ) ) {
/* signal was emitted, and no slots are used now, so
there was change from > 0 slots to 0 slots
*/
tb_with_filled_slots - - ;
} else {
/* signal was emitted, some slots are used now, so
there was a change from 0 slots to > 0
*/
tb_with_filled_slots + + ;
}
}
}
2017-09-18 12:39:17 -04:00
samplepos_t
2011-05-02 18:21:59 -04:00
Session : : compute_stop_limit ( ) const
{
2011-12-11 11:26:03 -05:00
if ( ! Config - > get_stop_at_session_end ( ) ) {
2017-09-18 12:39:17 -04:00
return max_samplepos ;
2011-12-11 11:26:03 -05:00
}
2013-01-24 07:52:41 -05:00
2018-09-18 18:51:59 -04:00
if ( config . get_external_sync ( ) ) {
2017-09-18 12:39:17 -04:00
return max_samplepos ;
2013-01-24 07:52:41 -05:00
}
2011-12-11 11:26:03 -05:00
bool const punching_in = ( config . get_punch_in ( ) & & _locations - > auto_punch_location ( ) ) ;
bool const punching_out = ( config . get_punch_out ( ) & & _locations - > auto_punch_location ( ) ) ;
if ( actively_recording ( ) ) {
/* permanently recording */
2017-09-18 12:39:17 -04:00
return max_samplepos ;
2011-12-11 11:26:03 -05:00
} else if ( punching_in & & ! punching_out ) {
/* punching in but never out */
2017-09-18 12:39:17 -04:00
return max_samplepos ;
} else if ( punching_in & & punching_out & & _locations - > auto_punch_location ( ) - > end ( ) > current_end_sample ( ) ) {
2011-12-11 11:26:03 -05:00
/* punching in and punching out after session end */
2017-09-18 12:39:17 -04:00
return max_samplepos ;
2011-05-02 18:21:59 -04:00
}
2022-03-14 14:18:20 -04:00
/* if there are any triggerboxen with slots filled, we ignore the end
* marker
*/
if ( tb_with_filled_slots ) {
return max_samplepos ;
}
2017-09-18 12:39:17 -04:00
return current_end_sample ( ) ;
2011-05-02 18:21:59 -04:00
}
2015-04-28 16:18:30 -04:00
/* dedicated thread for signal emission.
*
* while sending cross - thread signals from the process thread
* is fine in general , PBD : : Signal ' s use of boost : : function and
* boost : bind can produce a vast overhead which is not
* acceptable for low latency .
*
* This works around the issue by moving the boost overhead
* out of the RT thread . The overall load is probably higher but
* the realtime thread remains unaffected .
*/
void
2015-04-30 11:58:33 -04:00
Session : : emit_route_signals ( )
2015-04-28 16:18:30 -04:00
{
2015-04-30 11:58:33 -04:00
// TODO use RAII to allow using these signals in other places
BatchUpdateStart ( ) ; /* EMIT SIGNAL */
2020-04-18 17:54:24 -04:00
ProcessorChangeBlocker pcb ( this ) ;
2015-04-28 16:18:30 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : const_iterator ci = r - > begin ( ) ; ci ! = r - > end ( ) ; + + ci ) {
( * ci ) - > emit_pending_signals ( ) ;
}
2015-04-30 11:58:33 -04:00
BatchUpdateEnd ( ) ; /* EMIT SIGNAL */
2015-04-28 16:18:30 -04:00
}
void
Session : : emit_thread_start ( )
{
if ( _rt_thread_active ) {
return ;
}
_rt_thread_active = true ;
if ( pthread_create ( & _rt_emit_thread , NULL , emit_thread , this ) ) {
_rt_thread_active = false ;
}
}
void
Session : : emit_thread_terminate ( )
{
if ( ! _rt_thread_active ) {
return ;
}
_rt_thread_active = false ;
if ( pthread_mutex_lock ( & _rt_emit_mutex ) = = 0 ) {
pthread_cond_signal ( & _rt_emit_cond ) ;
pthread_mutex_unlock ( & _rt_emit_mutex ) ;
}
void * status ;
pthread_join ( _rt_emit_thread , & status ) ;
}
void *
Session : : emit_thread ( void * arg )
{
Session * s = static_cast < Session * > ( arg ) ;
2020-03-29 08:56:22 -04:00
pthread_set_name ( " SessionSignals " ) ;
2015-04-28 16:18:30 -04:00
s - > emit_thread_run ( ) ;
pthread_exit ( 0 ) ;
return 0 ;
}
void
Session : : emit_thread_run ( )
{
pthread_mutex_lock ( & _rt_emit_mutex ) ;
while ( _rt_thread_active ) {
emit_route_signals ( ) ;
pthread_cond_wait ( & _rt_emit_cond , & _rt_emit_mutex ) ;
}
pthread_mutex_unlock ( & _rt_emit_mutex ) ;
}
2018-09-18 18:51:59 -04:00
2020-03-20 19:38:23 -04:00
double
2020-03-20 21:26:51 -04:00
Session : : plan_master_strategy_engine ( pframes_t nframes , double master_speed , samplepos_t master_transport_sample , double /* catch_speed */ )
2018-09-18 18:51:59 -04:00
{
2020-03-20 19:38:23 -04:00
/* JACK Transport. */
2018-09-18 18:51:59 -04:00
TransportMasterManager & tmm ( TransportMasterManager : : instance ( ) ) ;
2020-03-20 19:38:23 -04:00
sampleoffset_t delta = _transport_sample - master_transport_sample ;
2020-03-20 21:26:51 -04:00
const bool interesting_transport_state_change_underway = ( locate_pending ( ) | | declick_in_progress ( ) ) ;
2018-09-18 18:51:59 -04:00
2020-03-22 12:57:38 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK Transport: delta = %1 transport change underway %2 master speed %3 \n " , delta , interesting_transport_state_change_underway , master_speed ) ) ;
2020-03-20 19:38:23 -04:00
if ( master_speed = = 0 ) {
2018-09-18 18:51:59 -04:00
2020-03-20 21:26:51 -04:00
DEBUG_TRACE ( DEBUG : : Slave , " JACK transport: not moving \n " ) ;
2020-03-22 12:57:38 -04:00
const samplecnt_t wlp = worst_latency_preroll_buffer_size_ceil ( ) ;
2020-03-20 19:38:23 -04:00
2020-03-22 12:57:38 -04:00
if ( delta ! = wlp ) {
2020-03-20 19:38:23 -04:00
2020-03-22 12:57:38 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK transport: need to locate to reduce delta %1 vs %2 \n " , delta , wlp ) ) ;
2020-03-20 19:38:23 -04:00
2020-03-22 12:57:38 -04:00
/* if we're not aligned with the current JACK * time, then jump to it */
2020-03-20 21:26:51 -04:00
2020-03-22 12:57:38 -04:00
if ( ! interesting_transport_state_change_underway ) {
2020-03-20 19:38:23 -04:00
2020-03-22 12:57:38 -04:00
const samplepos_t locate_target = master_transport_sample + wlp ;
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK transport: jump to master position %1 by locating to %2 \n " , master_transport_sample , locate_target ) ) ;
/* for JACK transport always stop after the locate (2nd argument == false) */
2020-03-20 19:38:23 -04:00
2020-03-22 12:57:38 -04:00
transport_master_strategy . action = TransportMasterLocate ;
transport_master_strategy . target = locate_target ;
transport_master_strategy . roll_disposition = MustStop ;
2020-03-20 21:26:51 -04:00
2020-03-22 12:57:38 -04:00
return 1.0 ;
2020-03-20 19:38:23 -04:00
2020-03-22 12:57:38 -04:00
} else {
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK Transport: locate already in process, master @ %1, locating %2 declick %3 \n " ,
master_transport_sample , locate_pending ( ) , declick_in_progress ( ) ) ) ;
transport_master_strategy . action = TransportMasterRelax ;
return 1.0 ;
2020-03-20 19:38:23 -04:00
}
}
} else {
2020-03-20 21:26:51 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK transport: MOVING at %1 \n " , master_speed ) ) ;
2020-03-22 12:57:38 -04:00
if ( _transport_fsm - > rolling ( ) ) {
2020-03-20 19:38:23 -04:00
/* master is rolling, and we're rolling ... with JACK we should always be perfectly in sync, so ... WTF? */
if ( delta ) {
if ( remaining_latency_preroll ( ) & & worst_latency_preroll ( ) ) {
/* our transport position is not moving because we're doing latency alignment. Nothing in particular to do */
2020-03-20 21:26:51 -04:00
DEBUG_TRACE ( DEBUG : : Slave , " JACK transport: waiting for latency alignment \n " ) ;
transport_master_strategy . action = TransportMasterRelax ;
return 1.0 ;
2020-03-20 19:38:23 -04:00
} else {
2020-03-22 12:57:38 -04:00
cerr < < " \n \n \n IMPOSSIBLE! OUT OF SYNC (delta = " < < delta < < " ) WITH JACK TRANSPORT (rlp = " < < remaining_latency_preroll ( ) < < " wlp " < < worst_latency_preroll ( ) < < " ) \n \n \n " ;
2020-03-20 19:38:23 -04:00
}
}
}
}
2020-03-20 21:26:51 -04:00
if ( ! interesting_transport_state_change_underway ) {
2020-03-20 19:38:23 -04:00
if ( master_speed ! = 0.0 ) {
/* master rolling, we should be too */
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) = = 0.0f ) {
2020-03-20 19:38:23 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave starts transport: %1 sample %2 tf %3 \n " , master_speed , master_transport_sample , _transport_sample ) ) ;
2020-03-20 21:26:51 -04:00
transport_master_strategy . action = TransportMasterStart ;
return 1.0 ;
2020-03-20 19:38:23 -04:00
}
} else if ( ! tmm . current ( ) - > starting ( ) ) { /* master stopped, not in "starting" state */
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) ! = 0.0f ) {
2020-03-20 19:38:23 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave stops transport: %1 sample %2 tf %3 \n " , master_speed , master_transport_sample , _transport_sample ) ) ;
2020-03-20 21:26:51 -04:00
transport_master_strategy . action = TransportMasterStop ;
return 1.0 ;
2020-03-20 19:38:23 -04:00
}
}
2018-09-18 18:51:59 -04:00
}
2020-03-20 21:26:51 -04:00
/* No varispeed with JACK */
transport_master_strategy . action = TransportMasterRelax ;
return 1.0 ;
2020-03-20 19:38:23 -04:00
}
double
Session : : plan_master_strategy ( pframes_t nframes , double master_speed , samplepos_t master_transport_sample , double catch_speed )
{
/* This is called from inside AudioEngine::process_callback(),
* immediately after the TransportMasterManager has run its
* : : pre_process_transport_masters ( ) method to allow all transport
* masters to update their information on the speed and position
* indicated by their data sources .
*
* Our task here is to determine what the Session should do during its
* process ( ) call in order to respond to the transport master ( or to
* not respond at all , if we ' re not using external sync ) . We want to
* set transport_master_strategy . action , which will be used from within
* the Session process ( ) callback ( via : : implement_master_strategy ( ) )
* to determine what , if anything to do there .
*
* The return value is the speed ( aka " ratio " ) to be used by the port
* resampler . If we ' re not chasing the master , the correct answer will
* be 1.0 . This can occur in a number of scenarios . If we are synced
* and locked to the master , we want to use the " catch speed " given to
* us as a parameter . This was determined by the
* TransportMasterManager as the correct speed to use in order to
* reduce the delta between the master ' s position and the session
* transport position .
*
* In situations where we are not synced + locked , either temporarily or
* longer term , we return 1.0 , which leads to no resampling , and the
* session will run at normal speed .
*/
if ( ! config . get_external_sync ( ) ) {
2021-06-22 13:29:45 -04:00
float desired = actual_speed ( ) ;
if ( desired = = 0.0 ) {
return _transport_fsm - > default_speed ( ) ;
}
return desired ;
2020-03-20 19:38:23 -04:00
}
2018-09-18 18:51:59 -04:00
2022-05-08 20:20:45 -04:00
/* When calling TransportMasterStart, should aim for
2020-05-09 17:42:26 -04:00
* delta > = _remaining_latency_preroll
* This way there can be silent pre - roll of exactly the delta time .
*
* In order to meet this condition , TransportMasterStart needs be set
* if the * end * of the current cycle can reach _remaining_latency_preroll .
* So current_block_size needs to be added here .
*/
const samplecnt_t wlp = worst_latency_preroll_buffer_size_ceil ( ) + current_block_size ;
2020-03-20 19:38:23 -04:00
TransportMasterManager & tmm ( TransportMasterManager : : instance ( ) ) ;
const samplecnt_t locate_threshold = 5 * current_block_size ;
if ( tmm . master_invalid_this_cycle ( ) ) {
DEBUG_TRACE ( DEBUG : : Slave , " session told not to use the transport master this cycle \n " ) ;
2020-03-23 21:08:36 -04:00
if ( _transport_fsm - > rolling ( ) & & Config - > get_transport_masters_just_roll_when_sync_lost ( ) ) {
transport_master_strategy . action = TransportMasterRelax ;
} else {
transport_master_strategy . action = TransportMasterNoRoll ;
}
2020-03-20 19:38:23 -04:00
return 1.0 ;
}
2018-09-18 18:51:59 -04:00
2019-11-15 17:57:13 -05:00
if ( tmm . current ( ) - > type ( ) = = Engine ) {
2020-03-20 19:38:23 -04:00
/* JACK is fundamentally different */
return plan_master_strategy_engine ( nframes , master_speed , master_transport_sample , catch_speed ) ;
}
2019-11-15 17:57:13 -05:00
2020-03-20 19:38:23 -04:00
const sampleoffset_t delta = _transport_sample - master_transport_sample ;
2019-11-15 17:57:13 -05:00
2020-03-20 19:38:23 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " \n \n \n \n session at %1, master at %2, delta: %3 res: %4 TFSM state %5 action %6 \n " , _transport_sample , master_transport_sample , delta , tmm . current ( ) - > resolution ( ) , _transport_fsm - > current_state ( ) , transport_master_strategy . action ) ) ;
2019-11-15 17:57:13 -05:00
2020-03-20 19:38:23 -04:00
const bool interesting_transport_state_change_underway = ( locate_pending ( ) | | declick_in_progress ( ) ) ;
2019-12-27 23:15:16 -05:00
2020-03-20 19:38:23 -04:00
if ( ( transport_master_strategy . action = = TransportMasterWait ) | | ( transport_master_strategy . action = = TransportMasterNoRoll ) ) {
2019-12-27 23:15:16 -05:00
2020-03-20 19:38:23 -04:00
/* We've either been:
*
* 1 ) waiting for the master to catch up with a position that
* we located to ( Wait )
* 2 ) waiting to be able to use the master ' s speed & position
*
* The two cases are very similar , but differ in the conditions
* under which we need to initiate a ( possibly successive )
* locate in response to the master ' s position
*
* This code is very similar to the non - wait case ( the " else "
* that ends this scope ) . The big difference is that here we
* know that we ' ve just finished a locate specifically in order
* to catch the master . This changes the logic a little bit .
*/
2019-12-27 23:15:16 -05:00
2020-03-20 19:38:23 -04:00
DEBUG_TRACE ( DEBUG : : Slave , " had been waiting for locate-to-catch-master to finish \n " ) ;
2019-12-27 23:15:16 -05:00
2020-03-20 19:38:23 -04:00
if ( interesting_transport_state_change_underway ) {
/* still waiting for the declick and/or locate to
finish . . . nothing to do for now .
*/
DEBUG_TRACE ( DEBUG : : Slave , " still waiting for the locate to finish \n " ) ;
return 1.0 ;
}
2019-12-27 23:15:16 -05:00
2020-03-20 19:38:23 -04:00
bool should_locate ;
2019-12-27 23:15:16 -05:00
2020-03-20 19:38:23 -04:00
if ( transport_master_strategy . action = = TransportMasterNoRoll ) {
2018-09-18 18:51:59 -04:00
2020-03-20 19:38:23 -04:00
/* We've been waiting to be able to use the master's
* position ( i . e to get a lock on the incoming data
* stream ) . We need to locate if we ' re either ahead or
* behind the master by < threshold > .
*/
should_locate = abs ( delta ) > locate_threshold ;
2019-12-27 23:15:16 -05:00
} else {
2020-03-20 19:38:23 -04:00
/* we located to be ahead of the master's position (see
* the locate call in the next " else " scope where we
* jump ahead by a significant distance ) .
*
* So , we should be ahead ( or behind ) the master ' s
* position , and waiting for it to get close to us .
*
* We only need to locate again if we are actually
* behind ( or ahead , for reverse motion ) of the master
* by more than < threshold > .
*/
should_locate = delta < 0 & & ( abs ( delta ) > locate_threshold ) ;
2018-09-18 18:51:59 -04:00
}
2019-11-15 17:57:13 -05:00
2020-03-20 19:38:23 -04:00
if ( should_locate ) {
/* we're too far from the master to catch it via
* varispeed . . . need to locate ahead of it , wait for
* it to get cose to us , then varispeed to sync .
*
* We assume that the transport state after the locate
* is always Stopped - we don ' t restart the transport
* until the master catches us , or at least gets close
* to our new position .
*
* Any time we locate , we need to reset the DLL used by
* the TransportMasterManager . Do that here , since the
* TMM will not need that again until after we start
* the locate ( and hence the apparent transport
* position of the Session will reflect the target we
* set here ) . That is because the locate will be
* initiated in the Session : : process ( ) callback that is
* about to happen right after we return .
*/
tmm . reinit ( master_speed , master_transport_sample ) ;
samplepos_t locate_target = master_transport_sample ;
2020-03-23 19:05:35 -04:00
/* locate to a position "worst_latency_preroll" head of
* the master , but also add in a generous estimate to
* cover the time it will take to locate to that
* position , based on our worst - case estimate for this
* session ( so far ) .
*/
2021-03-19 01:12:11 -04:00
locate_target + = wlp + lrintf ( ntracks ( ) * sample_rate ( ) * ( 1.5 * ( g_atomic_int_get ( & _current_usecs_per_track ) / 1000000.0 ) ) ) ;
2020-03-20 19:38:23 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " After locate-to-catch-master, still too far off (%1). Locate again to %2 \n " , delta , locate_target ) ) ;
transport_master_strategy . action = TransportMasterLocate ;
transport_master_strategy . target = locate_target ;
transport_master_strategy . roll_disposition = MustStop ;
transport_master_strategy . catch_speed = catch_speed ;
return 1.0 ;
}
if ( delta > wlp ) {
/* We're close, but haven't reached the point where we
* need to start rolling for preroll latency yet .
*/
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " master @ %1 is not yet within %2 of our position %3 (delta is %4) \n " , master_transport_sample , wlp , _transport_sample , delta ) ) ;
return 1.0 ;
}
2019-11-15 17:57:13 -05:00
2020-03-20 19:38:23 -04:00
/* case #3: we should start rolling */
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " master @ %1 is WITHIN %2 of our position %3 (delta is %4), so start \n " , master_transport_sample , wlp , _transport_sample , delta ) ) ;
2020-05-09 17:42:26 -04:00
if ( delta > _remaining_latency_preroll ) {
/* increase pre-roll to match delta. this allows
* to directly catch the transport w / o vari - speed */
_remaining_latency_preroll = delta ;
}
2020-03-20 19:38:23 -04:00
transport_master_strategy . action = TransportMasterStart ;
transport_master_strategy . catch_speed = catch_speed ;
return catch_speed ;
}
/* currently we're not waiting to sync with the master. So
* check if we ' re way out of alignment ( case # 1 ) or just a bit
* out of alignment ( case # 2 )
*/
if ( abs ( delta ) > locate_threshold ) {
/* CASE ONE
*
* This is a heuristic rather than a strictly provable rule . The idea
2019-11-15 17:57:13 -05:00
* is that if we ' re " far away " from the master , we should locate to its
* current position , and then varispeed to sync with it .
*
* On the other hand , if we ' re close to it , just varispeed .
*/
2020-03-20 19:38:23 -04:00
tmm . reinit ( master_speed , master_transport_sample ) ;
2019-12-27 12:49:14 -05:00
2020-03-20 19:38:23 -04:00
samplepos_t locate_target = master_transport_sample ;
2021-03-19 01:12:11 -04:00
locate_target + = wlp + lrintf ( ntracks ( ) * sample_rate ( ) * ( 1.5 * ( g_atomic_int_get ( & _current_usecs_per_track ) / 1000000.0 ) ) ) ;
2020-03-20 19:38:23 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " request locate to master position %1 \n " , locate_target ) ) ;
transport_master_strategy . action = TransportMasterLocate ;
transport_master_strategy . target = locate_target ;
transport_master_strategy . roll_disposition = ( master_speed ! = 0 ) ? MustRoll : MustStop ;
transport_master_strategy . catch_speed = catch_speed ;
/* Session::process_with(out)_events() will take this
* up when called .
*/
2019-12-27 12:49:14 -05:00
2020-03-20 19:38:23 -04:00
return 1.0 ;
} else if ( abs ( delta ) > tmm . current ( ) - > resolution ( ) ) {
/* CASE TWO
*
* If we ' re close , but not within the resolution of the
* master , just varispeed to chase the master , and be
* silent till we ' re synced
*/
tmm . block_disk_output ( ) ;
} else {
/* speed is set, we're locked and synced and good to go */
if ( ! locate_pending ( ) & & ! declick_in_progress ( ) ) {
DEBUG_TRACE ( DEBUG : : Slave , " master/slave synced & locked \n " ) ;
tmm . unblock_disk_output ( ) ;
2019-11-15 17:57:13 -05:00
}
2018-09-18 18:51:59 -04:00
}
2019-12-27 12:49:14 -05:00
if ( master_speed ! = 0.0 ) {
2020-03-20 19:38:23 -04:00
/* master rolling, we should be too */
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) = = 0.0f ) {
2019-12-27 12:49:14 -05:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave starts transport: %1 sample %2 tf %3 \n " , master_speed , master_transport_sample , _transport_sample ) ) ;
2020-03-20 19:38:23 -04:00
transport_master_strategy . action = TransportMasterStart ;
transport_master_strategy . catch_speed = catch_speed ;
return catch_speed ;
2018-09-18 18:51:59 -04:00
}
2019-11-15 17:57:13 -05:00
} else if ( ! tmm . current ( ) - > starting ( ) ) { /* master stopped, not in "starting" state */
2021-05-03 19:35:34 -04:00
if ( _transport_fsm - > transport_speed ( ) ! = 0.0f ) {
2019-12-27 12:49:14 -05:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave stops transport: %1 sample %2 tf %3 \n " , master_speed , master_transport_sample , _transport_sample ) ) ;
2020-03-20 19:38:23 -04:00
transport_master_strategy . action = TransportMasterStop ;
return catch_speed ;
2018-09-18 18:51:59 -04:00
}
2019-09-17 20:26:03 -04:00
}
2018-09-18 18:51:59 -04:00
2020-03-20 19:38:23 -04:00
/* we were not waiting for the master, we're close enough to
* it , and our transport state already matched the master
* ( stopped or rolling ) . We should just continue
* resampling / varispeeding at " catch_speed " in order to remain
* synced with the master .
2019-09-17 20:26:03 -04:00
*/
2018-09-18 18:51:59 -04:00
2020-03-20 19:38:23 -04:00
transport_master_strategy . action = TransportMasterRelax ;
return catch_speed ;
}
bool
Session : : implement_master_strategy ( )
{
/* This is called from within Session::process(), only if we are using
* external sync . The task here is simply to implement whatever actions
* where decided by : : plan_master_strategy ( ) , from within the
* : : process ( ) callback ( the planning step is executed before
* Session : : process ( ) begins .
*/
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " Implementing master strategy: %1 \n " , transport_master_strategy . action ) ) ;
switch ( transport_master_strategy . action ) {
case TransportMasterNoRoll :
/* This is the one case where we do not want the session to
call : : roll ( ) under any circumstances . Returning false here
will do that .
*/
return false ;
case TransportMasterRelax :
break ;
case TransportMasterWait :
break ;
case TransportMasterLocate :
transport_master_strategy . action = TransportMasterWait ;
2021-04-28 21:54:51 -04:00
TFSM_LOCATE ( transport_master_strategy . target , transport_master_strategy . roll_disposition , false , false ) ;
2020-03-20 19:38:23 -04:00
break ;
case TransportMasterStart :
TFSM_EVENT ( TransportFSM : : StartTransport ) ;
break ;
case TransportMasterStop :
2021-11-10 17:55:58 -05:00
cerr < < " MASTER STOP \n " ;
2020-03-20 19:38:23 -04:00
TFSM_STOP ( false , false ) ;
break ;
2018-09-18 18:51:59 -04:00
}
2019-09-17 20:26:03 -04:00
return true ;
2018-09-18 18:51:59 -04:00
}
2022-01-04 20:03:28 -05:00
void
Session : : sync_cues ( )
{
2022-01-05 12:26:10 -05:00
_locations - > apply ( * this , & Session : : sync_cues_from_list ) ;
2022-01-04 20:03:28 -05:00
}
2022-01-05 12:26:10 -05:00
struct LocationByTime
{
bool operator ( ) ( Location const * a , Location const * b ) {
return a - > start ( ) < b - > start ( ) ;
}
} ;
void
Session : : sync_cues_from_list ( Locations : : LocationList const & locs )
{
Locations : : LocationList sorted ( locs ) ;
LocationByTime cmp ;
sorted . sort ( cmp ) ;
2022-01-06 02:23:27 -05:00
CueEvents : : size_type n = 0 ;
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
/* this leaves the capacity unchanged */
_cue_events . clear ( ) ;
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
for ( auto const & loc : sorted ) {
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
if ( loc - > is_cue_marker ( ) ) {
_cue_events . push_back ( CueEvent ( loc - > cue_id ( ) , loc - > start_sample ( ) ) ) ;
}
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
if ( + + n > = _cue_events . capacity ( ) ) {
break ;
}
}
}
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
int32_t
2022-01-27 13:36:04 -05:00
Session : : first_cue_within ( samplepos_t s , samplepos_t e , bool & was_recorded )
2022-01-06 02:23:27 -05:00
{
int32_t active_cue = _active_cue . load ( ) ;
2022-01-05 12:26:10 -05:00
2022-01-27 13:36:04 -05:00
was_recorded = false ;
2022-01-06 02:23:27 -05:00
if ( active_cue > = 0 ) {
return active_cue ;
}
2022-01-05 12:26:10 -05:00
2022-02-19 11:55:59 -05:00
if ( ! ( config . get_cue_behavior ( ) & FollowCues ) ) {
2022-01-06 13:23:02 -05:00
return - 1 ;
}
2022-01-06 02:23:27 -05:00
CueEventTimeComparator cmp ;
CueEvents : : iterator si = lower_bound ( _cue_events . begin ( ) , _cue_events . end ( ) , s , cmp ) ;
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
if ( si ! = _cue_events . end ( ) ) {
if ( si - > time < e ) {
2022-01-27 13:36:04 -05:00
was_recorded = true ;
2022-01-06 02:23:27 -05:00
return si - > cue ;
2022-01-05 12:26:10 -05:00
}
}
2022-01-06 02:23:27 -05:00
return - 1 ;
}
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
void
2022-01-21 12:31:16 -05:00
Session : : cue_marker_change ( Location * /* ignored */ )
2022-01-06 02:23:27 -05:00
{
SessionEvent * ev = new SessionEvent ( SessionEvent : : SyncCues , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0.0 ) ;
queue_event ( ev ) ;
}
void
2022-09-26 16:48:30 -04:00
Session : : trigger_cue_row ( int32_t cue )
2022-01-06 02:23:27 -05:00
{
_pending_cue . store ( cue ) ;
2022-06-10 13:59:12 -04:00
request_transport_speed ( 1.0 ) ;
2022-01-06 02:23:27 -05:00
}
2022-10-01 08:38:58 -04:00
bool
Session : : bang_trigger_at ( int32_t route_index , int32_t row_index )
{
/* this is a convenience function for simple control surfaces to bang a trigger without any regards to banking */
int index = 0 ;
StripableList sl ;
get_stripables ( sl ) ;
sl . sort ( Stripable : : Sorter ( ) ) ;
for ( StripableList : : iterator s = sl . begin ( ) ; s ! = sl . end ( ) ; + + s ) {
boost : : shared_ptr < Route > r = boost : : dynamic_pointer_cast < Route > ( * s ) ;
if ( ! r | | ! r - > triggerbox ( ) ) {
continue ;
}
/* we're only interested in Trigger Tracks */
if ( ! ( r - > presentation_info ( ) . trigger_track ( ) ) ) {
continue ;
}
if ( index = = route_index ) {
r - > triggerbox ( ) - > bang_trigger_at ( row_index ) ;
2022-10-03 13:38:30 -04:00
return true ;
2022-10-01 08:38:58 -04:00
}
index + + ;
}
2022-10-03 13:38:30 -04:00
return false ;
2022-10-01 08:38:58 -04:00
}
bool
Session : : unbang_trigger_at ( int32_t route_index , int32_t row_index )
{
/* this is a convenience function for simple control surfaces to un-bang a trigger without any regards to banking */
int index = 0 ;
StripableList sl ;
get_stripables ( sl ) ;
sl . sort ( Stripable : : Sorter ( ) ) ;
for ( StripableList : : iterator s = sl . begin ( ) ; s ! = sl . end ( ) ; + + s ) {
boost : : shared_ptr < Route > r = boost : : dynamic_pointer_cast < Route > ( * s ) ;
if ( ! r | | ! r - > triggerbox ( ) ) {
continue ;
}
/* we're only interested in Trigger Tracks */
if ( ! ( r - > presentation_info ( ) . trigger_track ( ) ) ) {
continue ;
}
if ( index = = route_index ) {
r - > triggerbox ( ) - > unbang_trigger_at ( row_index ) ;
2022-10-03 13:38:30 -04:00
return true ;
2022-10-01 08:38:58 -04:00
}
index + + ;
}
2022-10-03 13:38:30 -04:00
return false ;
2022-10-01 08:38:58 -04:00
}
2022-10-04 13:04:33 -04:00
boost : : shared_ptr < TriggerBox >
Session : : triggerbox_at ( int32_t route_index ) const
{
int index = 0 ;
StripableList sl ;
get_stripables ( sl ) ;
sl . sort ( Stripable : : Sorter ( ) ) ;
for ( StripableList : : iterator s = sl . begin ( ) ; s ! = sl . end ( ) ; + + s ) {
boost : : shared_ptr < Route > r = boost : : dynamic_pointer_cast < Route > ( * s ) ;
if ( ! r | | ! r - > triggerbox ( ) ) {
continue ;
}
/* we're only interested in Trigger Tracks */
if ( ! ( r - > presentation_info ( ) . trigger_track ( ) ) ) {
continue ;
}
if ( index = = route_index ) {
return r - > triggerbox ( ) ;
}
index + + ;
}
return boost : : shared_ptr < TriggerBox > ( ) ;
}
TriggerPtr
Session : : trigger_at ( int32_t route_index , int32_t trigger_index ) const
{
boost : : shared_ptr < TriggerBox > tb = triggerbox_at ( route_index ) ;
if ( tb ) {
return tb - > trigger ( trigger_index ) ;
}
return TriggerPtr ( ) ;
}
2022-01-06 02:23:27 -05:00
void
Session : : maybe_find_pending_cue ( )
{
int32_t ac = _pending_cue . exchange ( - 1 ) ;
if ( ac > = 0 ) {
_active_cue . store ( ac ) ;
2022-01-21 12:11:32 -05:00
if ( TriggerBox : : cue_recording ( ) ) {
CueRecord cr ( ac , _transport_sample ) ;
TriggerBox : : cue_records . write ( & cr , 1 ) ;
/* failure is acceptable, but unlikely */
}
2022-01-05 12:26:10 -05:00
}
2022-01-06 02:23:27 -05:00
}
2022-01-05 12:26:10 -05:00
2022-01-06 02:23:27 -05:00
void
Session : : clear_active_cue ( )
{
_active_cue . store ( - 1 ) ;
2022-01-05 12:26:10 -05:00
}