2008-06-02 21:41:35 +00:00
/*
2019-08-03 04:01:25 +02: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 21:41:35 +00:00
# include <cmath>
# include <cerrno>
# include <algorithm>
# include <unistd.h>
2019-09-17 18:26:03 -06:00
# include <boost/algorithm/string/erase.hpp>
2019-03-14 01:11:10 +01:00
2019-09-17 18:26:03 -06:00
# include "pbd/i18n.h"
2009-02-25 18:26:51 +00:00
# include "pbd/error.h"
2009-12-04 02:15:12 +00:00
# include "pbd/enumwriter.h"
2020-03-29 14:56:22 +02:00
# include "pbd/pthread_utils.h"
2008-06-02 21:41:35 +00:00
2012-07-25 17:48:55 +00:00
# include <glibmm/threads.h>
2008-06-02 21:41:35 +00:00
2009-02-25 18:26:51 +00:00
# include "ardour/audioengine.h"
# include "ardour/auditioner.h"
2009-10-24 00:39:28 +00:00
# include "ardour/butler.h"
2012-05-24 06:09:29 +00:00
# include "ardour/cycle_timer.h"
2009-11-30 16:12:13 +00:00
# include "ardour/debug.h"
2017-07-20 17:53:56 -04:00
# include "ardour/disk_reader.h"
2012-05-24 06:09:29 +00:00
# include "ardour/graph.h"
# include "ardour/port.h"
2010-04-13 20:48:33 +00:00
# include "ardour/process_thread.h"
2014-04-28 19:58:24 -04:00
# include "ardour/scene_changer.h"
2009-10-24 00:39:28 +00:00
# include "ardour/session.h"
2019-09-17 18:26:03 -06: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 13:22:55 +00:00
# include "ardour/ticker.h"
2012-05-24 06:09:29 +00:00
# include "ardour/types.h"
2017-07-15 20:45:49 +02:00
# include "ardour/vca.h"
# include "ardour/vca_manager.h"
2008-06-02 21:41:35 +00:00
2010-06-29 13:47:53 +00:00
# include "midi++/mmc.h"
2008-06-02 21:41:35 +00:00
using namespace ARDOUR ;
using namespace PBD ;
using namespace std ;
2019-09-20 09:38:09 -06:00
# define TFSM_EVENT(evtype) { _transport_fsm->enqueue (new TransportFSM::Event (evtype)); }
# define TFSM_STOP(abort,clear) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::StopTransport,abort,clear)); }
2020-02-20 00:25:25 -07:00
# define TFSM_SPEED(speed,abort,clear_state,as_default) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::SetSpeed,speed,abort,clear_state,as_default)); }
2020-01-17 15:26:01 -07:00
# define TFSM_LOCATE(target,ltd,flush,loop,force) { _transport_fsm->enqueue (new TransportFSM::Event (TransportFSM::Locate,target,ltd,flush,loop,force)); }
2019-09-19 22:33:43 -06:00
2019-03-14 01:11:10 +01:00
2008-06-02 21:41:35 +00: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 21:41:35 +00:00
*/
2011-03-07 19:06:42 +00:00
2008-06-02 21:41:35 +00:00
void
2010-12-03 22:26:29 +00:00
Session : : process ( pframes_t nframes )
2008-06-02 21:41:35 +00:00
{
2017-09-18 12:39:17 -04:00
samplepos_t transport_at_start = _transport_sample ;
2012-01-27 13:22:55 +00:00
2008-06-02 21:41:35 +00:00
_silent = false ;
2009-04-16 16:02:25 +00:00
if ( processing_blocked ( ) ) {
_silent = true ;
return ;
}
2008-06-02 21:41:35 +00:00
if ( non_realtime_work_pending ( ) ) {
2019-09-22 11:50:51 -06: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-24 00:39:28 +00:00
if ( ! _butler - > transport_work_requested ( ) ) {
2019-09-17 18:26:03 -06:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " done, waiting? %1 \n " , _transport_fsm - > waiting_for_butler ( ) ) ) ;
butler_completed_transport_work ( ) ;
} else {
2019-09-22 11:50:51 -06:00
DEBUG_TRACE ( DEBUG : : Butler , " doesn't seem to have finished yet (from view of RT thread) \n " ) ;
2009-10-14 16:10:01 +00:00
}
}
2010-06-29 13:47:53 +00:00
2010-12-14 18:13:37 +00:00
_engine . main_thread ( ) - > get_buffers ( ) ;
2009-04-16 16:02:25 +00:00
2008-06-02 21:41:35 +00:00
( this - > * process_function ) ( nframes ) ;
2009-10-14 16:10:01 +00:00
2015-04-28 22:18:30 +02:00
/* realtime-safe meter-position and processor-order changes
2015-04-26 06:00:08 +02:00
*
* ideally this would be done in
* Route : : process_output_buffers ( ) but various functions
* callig it hold a _processor_lock reader - lock
*/
2019-09-17 18:26:03 -06:00
bool one_or_more_routes_declicking = false ;
2020-04-18 23:54:24 +02: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 18:26:03 -06:00
}
2015-04-28 22:18:30 +02:00
}
2019-09-17 18:26:03 -06:00
2015-04-28 22:18:30 +02: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 06:00:08 +02:00
}
2019-09-17 18:26:03 -06: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-19 22:33:43 -06:00
TFSM_EVENT ( TransportFSM : : DeclickDone ) ;
2019-09-17 18:26:03 -06:00
}
2010-12-14 18:13:37 +00:00
_engine . main_thread ( ) - > drop_buffers ( ) ;
2010-04-13 20:48:33 +00:00
2017-09-18 12:39:17 -04:00
/* deliver MIDI clock. Note that we need to use the transport sample
2012-01-27 13:22:55 +00: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 05:05:31 +00:00
try {
2013-12-02 23:33:33 -05:00
if ( ! _silent & & ! _engine . freewheeling ( ) & & Config - > get_send_midi_clock ( ) & & ( transport_speed ( ) = = 1.0f | | transport_speed ( ) = = 0.0f ) & & midi_clock - > has_midi_port ( ) ) {
2013-08-07 22:22:11 -04:00
midi_clock - > tick ( transport_at_start , nframes ) ;
2012-01-27 13:22:55 +00:00
}
2014-04-28 19:58:24 -04:00
_scene_changer - > run ( transport_at_start , transport_at_start + nframes ) ;
2012-01-05 05:05:31 +00:00
} catch ( . . . ) {
2012-01-06 20:29:18 +00:00
/* don't bother with a message */
2012-01-05 05:05:31 +00:00
}
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
SendFeedback ( ) ; /* EMIT SIGNAL */
}
int
2010-12-03 22:26:29 +00:00
Session : : fail_roll ( pframes_t nframes )
2009-04-23 17:48:37 +00:00
{
return no_roll ( nframes ) ;
}
int
2010-12-03 22:26:29 +00:00
Session : : no_roll ( pframes_t nframes )
2008-06-02 21:41:35 +00:00
{
2012-01-14 22:02:59 +00:00
PT_TIMING_CHECK ( 4 ) ;
2015-10-05 16:17:49 +02:00
2019-11-14 21:50:00 +01:00
samplepos_t end_sample = _transport_sample + floor ( nframes * _transport_speed ) ;
2008-06-02 21:41:35 +00:00
int ret = 0 ;
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
if ( _click_io ) {
2009-04-23 17:48:37 +00:00
_click_io - > silence ( nframes ) ;
2008-06-02 21:41:35 +00:00
}
2017-07-15 20:45:49 +02: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 20:45:49 +02:00
}
2019-09-17 18:26:03 -06:00
_global_locate_pending = locate_pending ( ) ;
2012-01-22 12:28:49 +00:00
if ( _process_graph ) {
2011-04-04 22:46:48 +00:00
DEBUG_TRACE ( DEBUG : : ProcessThreads , " calling graph/no-roll \n " ) ;
2018-05-22 18:09:26 +02:00
_process_graph - > routes_no_roll ( nframes , _transport_sample , end_sample , non_realtime_work_pending ( ) ) ;
2011-04-04 22:46:48 +00:00
} else {
2012-01-14 22:02:59 +00:00
PT_TIMING_CHECK ( 10 ) ;
2011-04-04 22:46:48 +00: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 22:46:48 +00: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 22:46:48 +00:00
error < < string_compose ( _ ( " Session: error in no roll for %1 " ) , ( * i ) - > name ( ) ) < < endmsg ;
ret = - 1 ;
break ;
}
}
2012-01-14 22:02:59 +00:00
PT_TIMING_CHECK ( 11 ) ;
2011-04-04 22:46:48 +00:00
}
2008-06-02 21:41:35 +00:00
2012-01-14 22:02:59 +00:00
PT_TIMING_CHECK ( 5 ) ;
2008-06-02 21:41:35 +00:00
return ret ;
}
2012-01-30 20:58:17 +00:00
/** @param need_butler to be set to true by this method if it needs the butler,
2012-01-30 21:11:35 +00:00
* otherwise it must be left alone .
2012-01-30 20:58:17 +00:00
*/
2008-06-02 21:41:35 +00:00
int
2010-12-03 22:26:29 +00:00
Session : : process_routes ( pframes_t nframes , bool & need_butler )
2008-06-02 21:41:35 +00:00
{
2011-04-04 22:46:48 +00:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2017-09-18 12:39:17 -04:00
const samplepos_t start_sample = _transport_sample ;
const samplepos_t end_sample = _transport_sample + floor ( nframes * _transport_speed ) ;
2015-10-05 16:17:49 +02:00
2017-07-15 20:45:49 +02: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 20:45:49 +02:00
}
2019-09-17 18:26:03 -06:00
_global_locate_pending = locate_pending ( ) ;
2017-07-05 17:24:02 -04:00
2012-01-22 12:28:49 +00:00
if ( _process_graph ) {
2011-04-04 22:46:48 +00:00
DEBUG_TRACE ( DEBUG : : ProcessThreads , " calling graph/process-routes \n " ) ;
2018-05-22 18:09:26 +02:00
if ( _process_graph - > process_routes ( nframes , start_sample , end_sample , need_butler ) < 0 ) {
2015-01-29 18:57:01 -05:00
stop_transport ( ) ;
return - 1 ;
}
2011-04-04 22:46:48 +00: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 22:46:48 +00:00
continue ;
}
2012-01-30 20:58:17 +00:00
bool b = false ;
2018-05-22 18:09:26 +02:00
if ( ( ret = ( * i ) - > roll ( nframes , start_sample , end_sample , b ) ) < 0 ) {
2019-09-19 22:33:43 -06:00
TFSM_STOP ( false , false ) ;
2011-04-04 22:46:48 +00:00
return - 1 ;
}
2012-01-30 20:58:17 +00:00
if ( b ) {
2017-04-11 17:38:34 +01:00
DEBUG_TRACE ( DEBUG : : Butler , string_compose ( " %1 rolled and needs butler \n " , ( * i ) - > name ( ) ) ) ;
2012-01-30 20:58:17 +00:00
need_butler = true ;
}
2011-04-04 22:46:48 +00:00
}
}
2008-06-02 21:41:35 +00:00
return 0 ;
}
void
2010-04-21 20:42:22 +00:00
Session : : get_track_statistics ( )
2008-06-02 21:41:35 +00:00
{
float pworst = 1.0f ;
float cworst = 1.0f ;
2010-04-21 20:42:22 +00:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
2008-06-02 21:41:35 +00:00
2010-04-21 20:42:22 +00: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 21:41:35 +00:00
continue ;
}
2009-10-14 16:10:01 +00:00
2010-04-21 20:42:22 +00:00
pworst = min ( pworst , tr - > playback_buffer_load ( ) ) ;
cworst = min ( cworst , tr - > capture_buffer_load ( ) ) ;
2008-06-02 21:41:35 +00: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
{
if ( _transport_speed = = 0.0 | | _count_in_samples > 0 | | _remaining_latency_preroll > 0 ) {
/* 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 .
*/
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_speed ) ) ;
return false ;
}
pos_and_delta - = _transport_sample ;
return true ;
}
2020-03-05 20:47:19 +01: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 21:41:35 +00:00
/** Process callback used when the auditioner is not active */
void
2010-12-03 22:26:29 +00:00
Session : : process_with_events ( pframes_t nframes )
2008-06-02 21:41:35 +00:00
{
2012-01-14 22:02:59 +00:00
PT_TIMING_CHECK ( 3 ) ;
2015-10-05 16:17:49 +02:00
2010-09-17 16:24:22 +00:00
SessionEvent * ev ;
2010-12-03 22:26:29 +00:00
pframes_t this_nframes ;
2017-09-18 12:39:17 -04:00
samplepos_t end_sample ;
2009-01-10 08:42:07 +00:00
bool session_needs_butler = false ;
2017-09-18 12:39:17 -04:00
samplecnt_t samples_moved ;
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
/* make sure the auditioner is silent */
if ( auditioner ) {
2009-04-23 17:48:37 +00:00
auditioner - > silence ( nframes ) ;
2008-06-02 21:41:35 +00: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 16:10:01 +00:00
process them .
2008-06-02 21:41:35 +00:00
*/
while ( ! non_realtime_work_pending ( ) & & ! immediate_events . empty ( ) ) {
2009-12-04 02:15:12 +00:00
SessionEvent * ev = immediate_events . front ( ) ;
2008-06-02 21:41:35 +00:00
immediate_events . pop_front ( ) ;
process_event ( ev ) ;
}
2017-09-28 06:31:12 +02:00
/* only count-in when going to roll at speed 1.0 */
2017-01-17 20:43:55 +01:00
if ( _transport_speed ! = 1.0 & & _count_in_samples > 0 ) {
_count_in_samples = 0 ;
}
2017-09-28 06:31:12 +02:00
if ( _transport_speed = = 0.0 ) {
_remaining_latency_preroll = 0 ;
}
assert ( _count_in_samples = = 0 | | _remaining_latency_preroll = = 0 | | _count_in_samples = = _remaining_latency_preroll ) ;
2017-01-17 20:43:55 +01:00
2019-12-29 18:41:46 -07: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
2017-10-12 01:49:05 +02:00
while ( _count_in_samples > 0 | | _remaining_latency_preroll > 0 ) {
2017-09-28 06:31:12 +02: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 20:47:19 +01:00
/* process until next route in-point */
ns = calc_preroll_subcycle ( ns ) ;
2017-09-30 13:10:17 +02:00
2017-09-28 06:31:12 +02: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 20:43:55 +01:00
2017-09-28 06:31:12 +02: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 20:43:55 +01: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 20:43:55 +01: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 23:55:00 +00: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 ;
2011-01-04 03:35:10 +00:00
if ( _transport_speed ! = 0 ) {
_send_qf_mtc = (
Config - > get_send_mtc ( ) & &
_transport_speed > = ( 1 - tolerance ) & &
_transport_speed < = ( 1 + tolerance )
) ;
2011-04-04 22:46:48 +00:00
2011-01-04 03:35:10 +00: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 22:46:48 +00: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-04 03:35:10 +00: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 22:46:48 +00:00
2011-01-04 03:35:10 +00:00
_pframes_since_last_mtc + = nframes ;
2011-01-03 23:55:00 +00:00
}
2011-04-04 22:46:48 +00:00
2011-01-03 23:55:00 +00: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 21:41:35 +00:00
*/
2009-10-26 14:38:58 +00:00
if ( _send_timecode_update ) {
2017-09-18 12:39:17 -04:00
send_full_time_code ( _transport_sample , nframes ) ;
2008-06-02 21:41:35 +00:00
}
if ( ! process_can_proceed ( ) ) {
_silent = true ;
return ;
}
if ( events . empty ( ) | | next_event = = events . end ( ) ) {
2016-02-23 15:41:21 +01:00
try_run_lua ( nframes ) ; // also during export ?? ->move to process_without_events()
2017-01-02 17:03:33 +01: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 21:41:35 +00:00
}
2017-10-31 22:01:46 +01:00
assert ( _transport_speed = = 0 | | _transport_speed = = 1.0 | | _transport_speed = = - 1.0 ) ;
samples_moved = ( samplecnt_t ) nframes * _transport_speed ;
2020-02-21 11:53:53 -07:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
2009-01-10 08:42:07 +00:00
2017-09-18 12:39:17 -04:00
end_sample = _transport_sample + samples_moved ;
2008-06-02 21:41:35 +00:00
{
2009-12-04 02:15:12 +00:00
SessionEvent * this_event ;
2008-06-02 21:41:35 +00:00
Events : : iterator the_next_one ;
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
if ( ! process_can_proceed ( ) ) {
_silent = true ;
return ;
}
2009-10-14 16:10:01 +00:00
2018-09-18 18:51:59 -04:00
if ( ! _exporting & & config . get_external_sync ( ) ) {
2020-03-20 17:38:23 -06:00
if ( ! implement_master_strategy ( ) ) {
no_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
}
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
if ( _transport_speed = = 0 ) {
2009-04-23 17:48:37 +00:00
no_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
}
2009-10-14 16:10:01 +00:00
2015-04-24 04:39:57 +02:00
2017-09-18 12:39:17 -04:00
samplepos_t stop_limit = compute_stop_limit ( ) ;
2008-06-02 21:41:35 +00:00
if ( maybe_stop ( stop_limit ) ) {
2019-11-03 14:42:10 +01:00
if ( ! _exporting & & ! timecode_transmission_suspended ( ) ) {
send_midi_time_code_for_cycle ( _transport_sample , end_sample , nframes ) ;
}
2009-04-23 17:48:37 +00:00
no_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00: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 16:10:01 +00:00
process some audio
2009-04-23 17:48:37 +00:00
*/
2008-06-02 21:41:35 +00:00
while ( nframes ) {
this_nframes = nframes ; /* real (jack) time relative */
2017-09-18 12:39:17 -04:00
samples_moved = ( samplecnt_t ) floor ( _transport_speed * nframes ) ; /* transport relative */
2019-11-01 13:25:18 -06:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
2008-06-02 21:41:35 +00: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 21:41:35 +00: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 ) ;
2019-11-01 13:25:18 -06:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("sub-loop2 (for %4)plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed, enum_2_string (this_event->type)));
2017-09-18 12:39:17 -04:00
this_nframes = abs ( floor ( samples_moved / _transport_speed ) ) ;
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
2016-02-23 15:41:21 +01:00
try_run_lua ( this_nframes ) ;
2008-06-02 21:41:35 +00:00
if ( this_nframes ) {
2009-10-14 16:10:01 +00:00
2019-11-03 14:42:10 +01: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 16:10:01 +00:00
2010-03-27 14:41:24 +00:00
if ( process_routes ( this_nframes , session_needs_butler ) ) {
2009-04-23 17:48:37 +00:00
fail_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
}
2011-04-04 22:46:48 +00:00
2010-05-11 23:22:15 +00:00
get_track_statistics ( ) ;
2008-06-02 21:41:35 +00:00
nframes - = this_nframes ;
2009-10-14 16:10:01 +00: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 13:17:42 -07:00
} else {
DEBUG_TRACE ( DEBUG : : Transport , " no transport motion \n " ) ;
2008-06-02 21:41:35 +00:00
}
maybe_stop ( stop_limit ) ;
}
2014-01-03 18:36:00 +01:00
if ( nframes > 0 ) {
_engine . split_cycle ( this_nframes ) ;
}
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
/* now handle this event and all others scheduled for the same time */
2009-10-14 16:10:01 +00:00
2017-09-18 12:39:17 -04:00
while ( this_event & & this_event - > action_sample = = _transport_sample ) {
2008-06-02 21:41:35 +00: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 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
/* if an event left our state changing, do the right thing */
2009-04-16 16:02:25 +00:00
if ( nframes & & non_realtime_work_pending ( ) ) {
2009-04-23 17:48:37 +00:00
no_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
break ;
}
/* this is necessary to handle the case of seamless looping */
2017-09-18 12:39:17 -04:00
end_sample = _transport_sample + floor ( nframes * _transport_speed ) ;
2008-06-02 21:41:35 +00:00
}
set_next_event ( ) ;
} /* implicit release of route lock */
2009-10-24 00:39:28 +00:00
if ( session_needs_butler ) {
2017-04-11 17:38:34 +01:00
DEBUG_TRACE ( DEBUG : : Butler , " p-with-events: session needs butler, call it \n " ) ;
2009-10-24 00:39:28 +00:00
_butler - > summon ( ) ;
}
2008-06-02 21:41:35 +00: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 21:26:23 +00:00
return true ;
}
2008-06-02 21:41:35 +00:00
2009-01-01 21:26:23 +00:00
return false ;
}
2008-06-02 21:41:35 +00:00
void
2010-12-03 22:26:29 +00:00
Session : : process_without_events ( pframes_t nframes )
2008-06-02 21:41:35 +00:00
{
bool session_needs_butler = false ;
2017-09-18 12:39:17 -04:00
samplecnt_t samples_moved ;
2008-06-02 21:41:35 +00: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 17:38:23 -06:00
if ( ! implement_master_strategy ( ) ) {
no_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
}
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
2018-09-18 18:51:59 -04:00
assert ( _transport_speed = = 0 | | _transport_speed = = 1.0 | | _transport_speed = = - 1.0 ) ;
2008-06-02 21:41:35 +00:00
if ( _transport_speed = = 0 ) {
2020-02-21 11:53:53 -07: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 21:41:35 +00:00
return ;
2018-09-18 18:51:59 -04:00
} else {
2019-02-06 20:26:44 +01:00
samples_moved = ( samplecnt_t ) nframes * _transport_speed ;
2020-02-21 11:53:53 -07:00
// DEBUG_TRACE (DEBUG::Transport, string_compose ("plan to move transport by %1 (%2 @ %3)\n", samples_moved, nframes, _transport_speed));
2008-06-02 21:41:35 +00:00
}
2009-10-14 16:10:01 +00:00
2010-08-02 01:59:34 +00: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 21:41:35 +00:00
}
2017-09-18 12:39:17 -04:00
samplepos_t const stop_limit = compute_stop_limit ( ) ;
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
if ( maybe_stop ( stop_limit ) ) {
2017-06-27 15:33:27 -04:00
no_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
2009-04-23 17:48:37 +00:00
if ( maybe_sync_start ( nframes ) ) {
2008-06-02 21:41:35 +00:00
return ;
}
2017-09-18 12:39:17 -04:00
click ( _transport_sample , nframes ) ;
2008-06-02 21:41:35 +00:00
2010-03-27 14:41:24 +00:00
if ( process_routes ( nframes , session_needs_butler ) ) {
2009-04-23 17:48:37 +00:00
fail_roll ( nframes ) ;
2008-06-02 21:41:35 +00:00
return ;
}
2010-05-11 23:22:15 +00:00
get_track_statistics ( ) ;
2017-09-18 12:39:17 -04:00
if ( samples_moved < 0 ) {
decrement_transport_position ( - samples_moved ) ;
2019-09-17 18:26:03 -06: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 ) ;
2019-09-17 18:26:03 -06:00
//DEBUG_TRACE (DEBUG::Transport, string_compose ("INcrement transport by %1 to %2\n", samples_moved, _transport_sample));
2019-03-03 13:17:42 -07:00
} else {
DEBUG_TRACE ( DEBUG : : Transport , " no transport motion \n " ) ;
2008-06-02 21:41:35 +00:00
}
maybe_stop ( stop_limit ) ;
2009-10-24 00:39:28 +00:00
if ( session_needs_butler ) {
2017-04-11 17:38:34 +01:00
DEBUG_TRACE ( DEBUG : : Butler , " p-without-events: session needs butler, call it \n " ) ;
2009-10-24 00:39:28 +00:00
_butler - > summon ( ) ;
}
2008-06-02 21:41:35 +00: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 21:41:35 +00:00
*/
void
2010-12-03 22:26:29 +00:00
Session : : process_audition ( pframes_t nframes )
2008-06-02 21:41:35 +00:00
{
2009-12-04 02:15:12 +00:00
SessionEvent * ev ;
2008-06-02 21:41:35 +00:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2013-04-06 16:04:02 -04:00
if ( ! ( * i ) - > is_auditioner ( ) ) {
2009-04-23 17:48:37 +00:00
( * i ) - > silence ( nframes ) ;
2008-06-02 21:41:35 +00:00
}
}
/* run the auditioner, and if it says we need butler service, ask for it */
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
if ( auditioner - > play_audition ( nframes ) > 0 ) {
2017-04-11 17:38:34 +01:00
DEBUG_TRACE ( DEBUG : : Butler , " auditioner needs butler, call it \n " ) ;
2009-10-24 00:39:28 +00:00
_butler - > summon ( ) ;
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
2011-04-04 22:46:48 +00:00
/* if using a monitor section, run it because otherwise we don't hear anything */
2014-01-16 17:22:19 -05:00
if ( _monitor_out & & auditioner - > needs_monitor ( ) ) {
2018-05-22 18:09:26 +02:00
_monitor_out - > monitor_run ( _transport_sample , _transport_sample + nframes , nframes ) ;
2011-04-04 22:46:48 +00:00
}
2010-03-25 03:40:07 +00:00
2008-06-02 21:41:35 +00: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 16:10:01 +00:00
process them .
2008-06-02 21:41:35 +00:00
*/
while ( ! non_realtime_work_pending ( ) & & ! immediate_events . empty ( ) ) {
2009-12-04 02:15:12 +00:00
SessionEvent * ev = immediate_events . front ( ) ;
2008-06-02 21:41:35 +00:00
immediate_events . pop_front ( ) ;
process_event ( ev ) ;
}
2010-03-25 03:40:07 +00:00
if ( ! auditioner - > auditioning ( ) ) {
2008-06-02 21:41:35 +00:00
/* auditioner no longer active, so go back to the normal process callback */
process_function = & Session : : process_with_events ;
}
}
bool
2010-12-03 22:26:29 +00:00
Session : : maybe_sync_start ( pframes_t & nframes )
2008-06-02 21:41:35 +00:00
{
2010-12-03 22:26:29 +00:00
pframes_t sync_offset ;
2008-06-02 21:41:35 +00:00
if ( ! waiting_for_sync_offset ) {
return false ;
}
if ( _engine . get_sync_offset ( sync_offset ) & & sync_offset < nframes ) {
2008-09-10 15:03:30 +00:00
/* generate silence up to the sync point, then
adjust nframes + offset to reflect whatever
is left to do .
*/
2009-04-23 17:48:37 +00:00
no_roll ( sync_offset ) ;
2008-06-02 21:41:35 +00:00
nframes - = sync_offset ;
2011-03-11 02:55:52 +00:00
Port : : increment_global_port_buffer_offset ( sync_offset ) ;
2008-06-02 21:41:35 +00:00
waiting_for_sync_offset = false ;
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
if ( nframes = = 0 ) {
2008-09-10 15:03:30 +00:00
return true ; // done, nothing left to process
2008-06-02 21:41:35 +00:00
}
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
} else {
2008-09-10 15:03:30 +00:00
/* sync offset point is not within this process()
2009-10-14 16:10:01 +00:00
cycle , so just generate silence . and don ' t bother
2008-09-10 15:03:30 +00:00
with any fancy stuff here , just the minimal silence .
*/
2009-04-16 16:02:25 +00:00
_silent = true ;
2008-09-10 15:03:30 +00:00
if ( Config - > get_locate_while_waiting_for_sync ( ) ) {
2019-02-26 09:02:28 -07:00
DEBUG_TRACE ( DEBUG : : Transport , " micro-locate while waiting for sync \n " ) ;
2008-09-10 15:03:30 +00:00
if ( micro_locate ( nframes ) ) {
/* XXX ERROR !!! XXX */
}
}
return true ; // done, nothing left to process
2008-06-02 21:41:35 +00:00
}
return false ;
}
2009-12-04 02:15:12 +00:00
void
Session : : queue_event ( SessionEvent * ev )
{
2019-03-18 15:33:05 +01:00
if ( deletion_in_progress ( ) ) {
2009-12-04 02:15:12 +00:00
return ;
2019-03-18 15:33:05 +01:00
} else if ( loading ( ) ) {
2009-12-04 02:15:12 +00:00
merge_event ( ev ) ;
} else {
2017-07-04 20:44:33 +02:00
Glib : : Threads : : Mutex : : Lock lm ( rb_write_lock ) ;
2009-12-04 02:15:12 +00: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-04 02:15:12 +00: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-04 02:15:12 +00:00
break ;
}
}
}
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-04 02:15:12 +00:00
switch ( ev - > type ) {
case SessionEvent : : SetLoop :
2019-12-05 13:00:24 -07:00
set_play_loop ( ev - > yes_or_no , true ) ;
2009-12-04 02:15:12 +00:00
break ;
case SessionEvent : : AutoLoop :
if ( play_loop ) {
2019-11-01 14:04:16 -06:00
/* roll after locate, do not flush, set "for loop end" true
2012-06-22 14:27:51 +00:00
*/
2020-01-17 15:26:01 -07:00
TFSM_LOCATE ( ev - > target_sample , MustRoll , false , true , false ) ;
2009-12-04 02:15:12 +00:00
}
remove = false ;
del = false ;
break ;
case SessionEvent : : Locate :
2019-11-23 15:54:09 -07:00
/* args: do not roll after locate, clear state, not for loop, force */
2020-03-24 18:22:11 -06:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " sending locate to %1 to tfsm \n " , ev - > target_sample ) ) ;
2020-01-17 15:26:01 -07:00
TFSM_LOCATE ( ev - > target_sample , ev - > locate_transport_disposition , true , false , ev - > yes_or_no ) ;
2009-12-04 02:15:12 +00:00
_send_timecode_update = true ;
break ;
case SessionEvent : : LocateRoll :
2019-11-23 15:53:54 -07:00
/* args: roll after locate, clear state if not looping, not for loop, force */
2020-01-17 15:26:01 -07:00
TFSM_LOCATE ( ev - > target_sample , ev - > locate_transport_disposition , ! play_loop , false , ev - > yes_or_no ) ;
2009-12-04 02:15:12 +00: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 ( ) ) {
2020-01-17 15:26:01 -07:00
TFSM_LOCATE ( ev - > target_sample , MustRoll , true , 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-04 02:15:12 +00: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 ;
2020-01-17 15:26:01 -07:00
TFSM_LOCATE ( ev - > target2_sample , MustRoll , true , false , false ) ;
2019-11-02 16:11:56 -06:00
_send_timecode_update = true ;
2009-12-04 02:15:12 +00:00
break ;
2020-03-13 13:50:34 -06:00
case SessionEvent : : SetTransportSpeed :
2020-02-20 00:25:25 -07:00
TFSM_SPEED ( ev - > speed , ev - > yes_or_no , ev - > second_yes_or_no , ev - > third_yes_or_no ) ;
2009-12-04 02:15:12 +00:00
break ;
2018-09-18 18:51:59 -04:00
case SessionEvent : : SetTransportMaster :
TransportMasterManager : : instance ( ) . set_current ( ev - > transport_master ) ;
break ;
2009-12-04 02:15:12 +00:00
case SessionEvent : : PunchIn :
2017-09-18 12:39:17 -04:00
// cerr << "PunchIN at " << transport_sample() << endl;
2017-09-29 21:01:50 +02:00
if ( config . get_punch_in ( ) & & record_status ( ) = = Enabled ) {
2009-12-04 02:15:12 +00: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 21:01:50 +02:00
if ( config . get_punch_out ( ) ) {
2009-12-04 02:15:12 +00:00
step_back_from_record ( ) ;
}
remove = false ;
del = false ;
break ;
case SessionEvent : : RangeStop :
2019-09-19 22:33:43 -06:00
TFSM_STOP ( ev - > yes_or_no , false ) ;
2009-12-04 02:15:12 +00:00
remove = false ;
del = false ;
break ;
case SessionEvent : : RangeLocate :
2012-06-22 14:27:51 +00:00
/* args: roll after locate, do flush, not with loop */
2020-01-17 15:26:01 -07:00
TFSM_LOCATE ( ev - > target_sample , MustRoll , true , false , false ) ;
2009-12-04 02:15:12 +00:00
remove = false ;
del = false ;
break ;
case SessionEvent : : Overwrite :
2020-03-28 17:31:12 +01:00
if ( boost : : shared_ptr < Track > track = ev - > track . lock ( ) ) {
overwrite_some_buffers ( track , ev - > overwrite ) ;
}
2009-12-04 02:15:12 +00:00
break ;
2020-04-12 13:35:21 +02:00
case SessionEvent : : OverwriteAll :
overwrite_some_buffers ( boost : : shared_ptr < Track > ( ) , ev - > overwrite ) ;
break ;
2009-12-04 02:15:12 +00:00
case SessionEvent : : Audition :
set_audition ( ev - > region ) ;
// drop reference to region
ev - > region . reset ( ) ;
break ;
case SessionEvent : : SetPlayAudioRange :
set_play_range ( ev - > audio_range , ( ev - > speed = = 1.0f ) ) ;
break ;
2014-07-02 17:34:49 -05:00
case SessionEvent : : CancelPlayAudioRange :
unset_play_range ( ) ;
break ;
2009-12-07 21:37:35 +00:00
case SessionEvent : : RealTimeOperation :
process_rtop ( ev ) ;
del = false ; // other side of RT request needs to clean up
break ;
2011-04-04 22:46:48 +00:00
case SessionEvent : : AdjustPlaybackBuffering :
schedule_playback_buffering_adjustment ( ) ;
break ;
2010-06-09 17:24:07 +00:00
2011-04-04 22:46:48 +00:00
case SessionEvent : : AdjustCaptureBuffering :
schedule_capture_buffering_adjustment ( ) ;
break ;
2010-06-09 17:24:07 +00:00
2010-08-02 01:59:34 +00:00
case SessionEvent : : SetTimecodeTransmission :
g_atomic_int_set ( & _suspend_timecode_transmission , ev - > yes_or_no ? 0 : 1 ) ;
break ;
2009-12-04 02:15:12 +00:00
default :
fatal < < string_compose ( _ ( " Programming error: illegal event type in process_event (%1) " ) , ev - > type ) < < endmsg ;
2014-11-14 10:47:43 +01:00
abort ( ) ; /*NOTREACHED*/
2009-12-04 02:15:12 +00:00
break ;
} ;
if ( remove ) {
del = del & & ! _remove_event ( ev ) ;
}
if ( del ) {
delete ev ;
}
}
2009-12-07 21:37:35 +00:00
2017-09-18 12:39:17 -04:00
samplepos_t
2011-05-02 22:21:59 +00:00
Session : : compute_stop_limit ( ) const
{
2011-12-11 16:26:03 +00:00
if ( ! Config - > get_stop_at_session_end ( ) ) {
2017-09-18 12:39:17 -04:00
return max_samplepos ;
2011-12-11 16:26:03 +00:00
}
2013-01-24 12:52:41 +00: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 12:52:41 +00:00
}
2011-12-11 16:26:03 +00: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 16:26:03 +00: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 16:26:03 +00:00
/* punching in and punching out after session end */
2017-09-18 12:39:17 -04:00
return max_samplepos ;
2011-05-02 22:21:59 +00:00
}
2017-09-18 12:39:17 -04:00
return current_end_sample ( ) ;
2011-05-02 22:21:59 +00:00
}
2015-04-28 22:18:30 +02: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 17:58:33 +02:00
Session : : emit_route_signals ( )
2015-04-28 22:18:30 +02:00
{
2015-04-30 17:58:33 +02:00
// TODO use RAII to allow using these signals in other places
BatchUpdateStart ( ) ; /* EMIT SIGNAL */
2020-04-18 23:54:24 +02:00
ProcessorChangeBlocker pcb ( this ) ;
2015-04-28 22:18:30 +02: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 17:58:33 +02:00
BatchUpdateEnd ( ) ; /* EMIT SIGNAL */
2015-04-28 22:18:30 +02: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 14:56:22 +02:00
pthread_set_name ( " SessionSignals " ) ;
2015-04-28 22:18:30 +02: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 17:38:23 -06:00
double
2020-03-20 19:26:51 -06: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 17:38:23 -06:00
/* JACK Transport. */
2018-09-18 18:51:59 -04:00
TransportMasterManager & tmm ( TransportMasterManager : : instance ( ) ) ;
2020-03-20 17:38:23 -06:00
sampleoffset_t delta = _transport_sample - master_transport_sample ;
2020-03-20 19:26:51 -06:00
const bool interesting_transport_state_change_underway = ( locate_pending ( ) | | declick_in_progress ( ) ) ;
2018-09-18 18:51:59 -04:00
2020-03-22 10:57:38 -06: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 17:38:23 -06:00
if ( master_speed = = 0 ) {
2018-09-18 18:51:59 -04:00
2020-03-20 19:26:51 -06:00
DEBUG_TRACE ( DEBUG : : Slave , " JACK transport: not moving \n " ) ;
2020-03-22 10:57:38 -06:00
const samplecnt_t wlp = worst_latency_preroll_buffer_size_ceil ( ) ;
2020-03-20 17:38:23 -06:00
2020-03-22 10:57:38 -06:00
if ( delta ! = wlp ) {
2020-03-20 17:38:23 -06:00
2020-03-22 10:57:38 -06:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK transport: need to locate to reduce delta %1 vs %2 \n " , delta , wlp ) ) ;
2020-03-20 17:38:23 -06:00
2020-03-22 10:57:38 -06:00
/* if we're not aligned with the current JACK * time, then jump to it */
2020-03-20 19:26:51 -06:00
2020-03-22 10:57:38 -06:00
if ( ! interesting_transport_state_change_underway ) {
2020-03-20 17:38:23 -06:00
2020-03-22 10:57:38 -06: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 17:38:23 -06:00
2020-03-22 10:57:38 -06:00
transport_master_strategy . action = TransportMasterLocate ;
transport_master_strategy . target = locate_target ;
transport_master_strategy . roll_disposition = MustStop ;
2020-03-20 19:26:51 -06:00
2020-03-22 10:57:38 -06:00
return 1.0 ;
2020-03-20 17:38:23 -06:00
2020-03-22 10:57:38 -06: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 17:38:23 -06:00
}
}
} else {
2020-03-20 19:26:51 -06:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " JACK transport: MOVING at %1 \n " , master_speed ) ) ;
2020-03-22 10:57:38 -06:00
if ( _transport_fsm - > rolling ( ) ) {
2020-03-20 17:38:23 -06: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 19:26:51 -06:00
DEBUG_TRACE ( DEBUG : : Slave , " JACK transport: waiting for latency alignment \n " ) ;
transport_master_strategy . action = TransportMasterRelax ;
return 1.0 ;
2020-03-20 17:38:23 -06:00
} else {
2020-03-22 10:57:38 -06: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 17:38:23 -06:00
}
}
}
}
2020-03-20 19:26:51 -06:00
if ( ! interesting_transport_state_change_underway ) {
2020-03-20 17:38:23 -06:00
if ( master_speed ! = 0.0 ) {
/* master rolling, we should be too */
if ( _transport_speed = = 0.0f ) {
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:26:51 -06:00
transport_master_strategy . action = TransportMasterStart ;
return 1.0 ;
2020-03-20 17:38:23 -06:00
}
} else if ( ! tmm . current ( ) - > starting ( ) ) { /* master stopped, not in "starting" state */
if ( _transport_speed ! = 0.0f ) {
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:26:51 -06:00
transport_master_strategy . action = TransportMasterStop ;
return 1.0 ;
2020-03-20 17:38:23 -06:00
}
}
2018-09-18 18:51:59 -04:00
}
2020-03-20 19:26:51 -06:00
/* No varispeed with JACK */
transport_master_strategy . action = TransportMasterRelax ;
return 1.0 ;
2020-03-20 17:38:23 -06: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 ( ) ) {
2020-03-27 10:24:19 -06:00
return actual_speed ( ) ;
2020-03-20 17:38:23 -06:00
}
2018-09-18 18:51:59 -04:00
2020-05-09 23:42:26 +02:00
/* When calling TransportMasterStart, sould aim for
* 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 17:38:23 -06: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 19:08:36 -06: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 17:38:23 -06:00
return 1.0 ;
}
2018-09-18 18:51:59 -04:00
2019-11-15 15:57:13 -07:00
if ( tmm . current ( ) - > type ( ) = = Engine ) {
2020-03-20 17:38:23 -06:00
/* JACK is fundamentally different */
return plan_master_strategy_engine ( nframes , master_speed , master_transport_sample , catch_speed ) ;
}
2019-11-15 15:57:13 -07:00
2020-03-20 17:38:23 -06:00
const sampleoffset_t delta = _transport_sample - master_transport_sample ;
2019-11-15 15:57:13 -07:00
2020-03-20 17:38:23 -06: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 15:57:13 -07:00
2020-03-20 17:38:23 -06:00
const bool interesting_transport_state_change_underway = ( locate_pending ( ) | | declick_in_progress ( ) ) ;
2019-12-27 21:15:16 -07:00
2020-03-20 17:38:23 -06:00
if ( ( transport_master_strategy . action = = TransportMasterWait ) | | ( transport_master_strategy . action = = TransportMasterNoRoll ) ) {
2019-12-27 21:15:16 -07:00
2020-03-20 17:38:23 -06: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 21:15:16 -07:00
2020-03-20 17:38:23 -06:00
DEBUG_TRACE ( DEBUG : : Slave , " had been waiting for locate-to-catch-master to finish \n " ) ;
2019-12-27 21:15:16 -07:00
2020-03-20 17:38:23 -06: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 21:15:16 -07:00
2020-03-20 17:38:23 -06:00
bool should_locate ;
2019-12-27 21:15:16 -07:00
2020-03-20 17:38:23 -06:00
if ( transport_master_strategy . action = = TransportMasterNoRoll ) {
2018-09-18 18:51:59 -04:00
2020-03-20 17:38:23 -06: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 21:15:16 -07:00
} else {
2020-03-20 17:38:23 -06: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 15:57:13 -07:00
2020-03-20 17:38:23 -06: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 17:05:35 -06: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 ) .
*/
locate_target + = wlp + lrintf ( ntracks ( ) * sample_rate ( ) * ( 1.5 * ( current_usecs_per_track / 1000000.0 ) ) ) ;
2020-03-20 17:38:23 -06: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 15:57:13 -07:00
2020-03-20 17:38:23 -06: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 23:42:26 +02: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 17:38:23 -06: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 15:57:13 -07: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 17:38:23 -06:00
tmm . reinit ( master_speed , master_transport_sample ) ;
2019-12-27 10:49:14 -07:00
2020-03-20 17:38:23 -06:00
samplepos_t locate_target = master_transport_sample ;
2020-05-09 23:42:26 +02:00
locate_target + = wlp + lrintf ( ntracks ( ) * sample_rate ( ) * ( 1.5 * ( current_usecs_per_track / 1000000.0 ) ) ) ;
2020-03-20 17:38:23 -06: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 10:49:14 -07:00
2020-03-20 17:38:23 -06: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 15:57:13 -07:00
}
2018-09-18 18:51:59 -04:00
}
2019-12-27 10:49:14 -07:00
if ( master_speed ! = 0.0 ) {
2020-03-20 17:38:23 -06:00
/* master rolling, we should be too */
2019-09-17 18:26:03 -06:00
if ( _transport_speed = = 0.0f ) {
2019-12-27 10:49:14 -07: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 17:38:23 -06: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 15:57:13 -07:00
} else if ( ! tmm . current ( ) - > starting ( ) ) { /* master stopped, not in "starting" state */
2018-09-18 18:51:59 -04:00
if ( _transport_speed ! = 0.0f ) {
2019-12-27 10:49:14 -07: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 17:38:23 -06:00
transport_master_strategy . action = TransportMasterStop ;
return catch_speed ;
2018-09-18 18:51:59 -04:00
}
2019-09-17 18:26:03 -06:00
}
2018-09-18 18:51:59 -04:00
2020-03-20 17:38:23 -06: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 18:26:03 -06:00
*/
2018-09-18 18:51:59 -04:00
2020-03-20 17:38:23 -06: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 ;
TFSM_LOCATE ( transport_master_strategy . target , transport_master_strategy . roll_disposition , true , false , false ) ;
break ;
case TransportMasterStart :
TFSM_EVENT ( TransportFSM : : StartTransport ) ;
break ;
case TransportMasterStop :
TFSM_STOP ( false , false ) ;
break ;
2018-09-18 18:51:59 -04:00
}
2019-09-17 18:26:03 -06:00
return true ;
2018-09-18 18:51:59 -04:00
}