2008-06-02 17:41:35 -04:00
/*
2008-08-04 18:37:24 -04:00
Copyright ( C ) 1999 - 2003 Paul Davis
2008-06-02 17:41:35 -04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2011-02-15 13:47:10 -05:00
# ifdef WAF_BUILD
# include "libardour-config.h"
# endif
2012-05-24 02:09:29 -04:00
# include <cmath>
# include <cerrno>
# include <unistd.h>
2008-06-02 17:41:35 -04:00
2009-02-25 13:26:51 -05:00
# include "pbd/undo.h"
# include "pbd/error.h"
2009-11-30 11:12:13 -05:00
# include "pbd/enumwriter.h"
2009-02-25 13:26:51 -05:00
# include "pbd/pthread_utils.h"
# include "pbd/memento_command.h"
2014-04-17 09:47:06 -04:00
# include "pbd/stacktrace.h"
2008-06-02 17:41:35 -04:00
2009-02-25 13:26:51 -05:00
# include "midi++/mmc.h"
# include "midi++/port.h"
2008-06-02 17:41:35 -04:00
2009-10-23 19:23:00 -04:00
# include "ardour/audioengine.h"
2009-02-25 13:26:51 -05:00
# include "ardour/auditioner.h"
2017-01-23 07:25:24 -05:00
# include "ardour/automation_watch.h"
2009-10-23 19:23:00 -04:00
# include "ardour/butler.h"
2011-10-18 23:34:02 -04:00
# include "ardour/click.h"
2009-11-30 11:12:13 -05:00
# include "ardour/debug.h"
2017-07-27 17:27:49 -04:00
# include "ardour/disk_reader.h"
2009-02-25 13:26:51 -05:00
# include "ardour/location.h"
2018-02-11 10:39:36 -05:00
# include "ardour/playlist.h"
2014-04-17 09:47:06 -04:00
# include "ardour/profile.h"
2014-05-02 18:43:37 -04:00
# include "ardour/scene_changer.h"
2009-10-23 19:23:00 -04:00
# include "ardour/session.h"
2018-09-18 18:51:59 -04:00
# include "ardour/transport_master.h"
# include "ardour/transport_master_manager.h"
2017-01-17 14:43:55 -05:00
# include "ardour/tempo.h"
2011-01-19 12:38:56 -05:00
# include "ardour/operations.h"
2017-07-21 15:46:36 -04:00
# include "ardour/vca.h"
# include "ardour/vca_manager.h"
2008-06-02 17:41:35 -04:00
2016-07-14 14:44:52 -04:00
# include "pbd/i18n.h"
2008-06-02 17:41:35 -04:00
using namespace std ;
using namespace ARDOUR ;
using namespace PBD ;
2009-11-08 11:28:21 -05:00
void
Session : : add_post_transport_work ( PostTransportWork ptw )
{
PostTransportWork oldval ;
PostTransportWork newval ;
int tries = 0 ;
while ( tries < 8 ) {
oldval = ( PostTransportWork ) g_atomic_int_get ( & _post_transport_work ) ;
newval = PostTransportWork ( oldval | ptw ) ;
if ( g_atomic_int_compare_and_exchange ( & _post_transport_work , oldval , newval ) ) {
/* success */
return ;
}
}
error < < " Could not set post transport work! Crazy thread madness, call the programmers " < < endmsg ;
}
2018-09-18 18:51:59 -04:00
bool
Session : : should_ignore_transport_request ( TransportRequestSource src , TransportRequestType type ) const
2008-06-02 17:41:35 -04:00
{
2018-09-18 18:51:59 -04:00
if ( config . get_external_sync ( ) ) {
if ( TransportMasterManager : : instance ( ) . current ( ) - > allow_request ( src , type ) ) {
return false ;
} else {
return true ;
}
2008-06-02 17:41:35 -04:00
}
2018-09-18 18:51:59 -04:00
return false ;
}
2009-10-30 14:14:25 -04:00
2018-10-05 12:35:10 -04:00
bool
Session : : synced_to_engine ( ) const {
return config . get_external_sync ( ) & & TransportMasterManager : : instance ( ) . current ( ) - > type ( ) = = Engine ;
}
2018-09-18 18:51:59 -04:00
void
Session : : request_sync_source ( boost : : shared_ptr < TransportMaster > tm )
{
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetTransportMaster , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0.0 ) ;
ev - > transport_master = tm ;
DEBUG_TRACE ( DEBUG : : Slave , " sent request for new transport master \n " ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
2018-09-18 18:51:59 -04:00
Session : : request_transport_speed ( double speed , bool as_default , TransportRequestSource origin )
2008-06-02 17:41:35 -04:00
{
2018-09-18 18:51:59 -04:00
if ( should_ignore_transport_request ( origin , TR_Speed ) ) {
return ;
}
2009-12-03 21:15:12 -05:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetTransportSpeed , SessionEvent : : Add , SessionEvent : : Immediate , 0 , speed ) ;
2015-12-13 12:16:57 -05:00
ev - > third_yes_or_no = as_default ; // as_default
2012-06-22 10:45:44 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Request transport speed = %1 as default = %2 \n " , speed , as_default ) ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
2011-02-06 20:12:47 -05:00
/** Request a new transport speed, but if the speed parameter is exactly zero then use
* a very small + ve value to prevent the transport actually stopping . This method should
* be used by callers who are varying transport speed but don ' t ever want to stop it .
*/
void
2018-09-18 18:51:59 -04:00
Session : : request_transport_speed_nonzero ( double speed , bool as_default , TransportRequestSource origin )
2011-02-06 20:12:47 -05:00
{
2018-09-18 18:51:59 -04:00
if ( should_ignore_transport_request ( origin , TransportRequestType ( TR_Speed | TR_Start ) ) ) {
return ;
}
2011-02-06 20:12:47 -05:00
if ( speed = = 0 ) {
speed = DBL_EPSILON ;
}
2012-06-22 10:45:44 -04:00
request_transport_speed ( speed , as_default ) ;
2011-02-06 20:12:47 -05:00
}
2008-06-02 17:41:35 -04:00
void
2018-09-18 18:51:59 -04:00
Session : : request_stop ( bool abort , bool clear_state , TransportRequestSource origin )
2008-06-02 17:41:35 -04:00
{
2018-09-18 18:51:59 -04:00
if ( should_ignore_transport_request ( origin , TR_Stop ) ) {
return ;
}
2017-09-18 12:39:17 -04:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetTransportSpeed , SessionEvent : : Add , SessionEvent : : Immediate , audible_sample ( ) , 0.0 , abort , clear_state ) ;
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Request transport stop, audible %3 transport %4 abort = %1, clear state = %2 \n " , abort , clear_state , audible_sample ( ) , _transport_sample ) ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
2018-09-18 18:51:59 -04:00
Session : : request_locate ( samplepos_t target_sample , bool with_roll , TransportRequestSource origin )
2008-06-02 17:41:35 -04:00
{
2018-09-18 18:51:59 -04:00
if ( should_ignore_transport_request ( origin , TR_Locate ) ) {
return ;
}
2017-09-18 12:39:17 -04:00
SessionEvent * ev = new SessionEvent ( with_roll ? SessionEvent : : LocateRoll : SessionEvent : : Locate , SessionEvent : : Add , SessionEvent : : Immediate , target_sample , 0 , false ) ;
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Request locate to %1 \n " , target_sample ) ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
2017-09-18 12:39:17 -04:00
Session : : force_locate ( samplepos_t target_sample , bool with_roll )
2008-06-02 17:41:35 -04:00
{
2017-09-18 12:39:17 -04:00
SessionEvent * ev = new SessionEvent ( with_roll ? SessionEvent : : LocateRoll : SessionEvent : : Locate , SessionEvent : : Add , SessionEvent : : Immediate , target_sample , 0 , true ) ;
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Request forced locate to %1 \n " , target_sample ) ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
2017-01-19 07:03:57 -05:00
void
Session : : unset_preroll_record_trim ( )
{
_preroll_record_trim_len = 0 ;
}
void
2017-09-18 12:39:17 -04:00
Session : : request_preroll_record_trim ( samplepos_t rec_in , samplecnt_t preroll )
2017-01-19 07:03:57 -05:00
{
if ( actively_recording ( ) ) {
return ;
}
unset_preroll_record_trim ( ) ;
config . set_punch_in ( false ) ;
config . set_punch_out ( false ) ;
2017-09-18 12:39:17 -04:00
samplepos_t pos = std : : max ( ( samplepos_t ) 0 , rec_in - preroll ) ;
2017-01-19 07:03:57 -05:00
_preroll_record_trim_len = preroll ;
maybe_enable_record ( ) ;
request_locate ( pos , true ) ;
2017-09-18 12:39:17 -04:00
set_requested_return_sample ( rec_in ) ;
2017-01-19 07:03:57 -05:00
}
2017-02-13 16:59:20 -05:00
void
Session : : request_count_in_record ( )
{
if ( actively_recording ( ) ) {
return ;
}
if ( transport_rolling ( ) ) {
return ;
}
maybe_enable_record ( ) ;
_count_in_once = true ;
request_transport_speed ( 1.0 , true ) ;
}
2008-06-02 17:41:35 -04:00
void
2014-04-17 09:47:06 -04:00
Session : : request_play_loop ( bool yn , bool change_transport_roll )
2008-06-02 17:41:35 -04:00
{
2018-09-18 18:51:59 -04:00
if ( transport_master_is_external ( ) & & yn ) {
2015-12-15 02:39:09 -05:00
// don't attempt to loop when not using Internal Transport
// see also gtk2_ardour/ardour_ui_options.cc parameter_changed()
return ;
}
2009-12-03 21:15:12 -05:00
SessionEvent * ev ;
2010-08-09 12:40:31 -04:00
Location * location = _locations - > auto_loop_location ( ) ;
2014-04-17 09:47:06 -04:00
double target_speed ;
2008-06-02 17:41:35 -04:00
if ( location = = 0 & & yn ) {
error < < _ ( " Cannot loop - no loop range defined " )
< < endmsg ;
return ;
}
2014-04-17 09:47:06 -04:00
if ( change_transport_roll ) {
if ( transport_rolling ( ) ) {
/* start looping at current speed */
target_speed = transport_speed ( ) ;
} else {
/* currently stopped */
if ( yn ) {
/* start looping at normal speed */
target_speed = 1.0 ;
} else {
target_speed = 0.0 ;
}
}
} else {
/* leave the speed alone */
target_speed = transport_speed ( ) ;
}
ev = new SessionEvent ( SessionEvent : : SetLoop , SessionEvent : : Add , SessionEvent : : Immediate , 0 , target_speed , yn ) ;
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Request set loop = %1, change roll state ? %2 \n " , yn , change_transport_roll ) ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
2014-04-17 09:47:06 -04:00
if ( yn ) {
if ( ! change_transport_roll ) {
if ( ! transport_rolling ( ) ) {
/* we're not changing transport state, but we do want
to set up position for the new loop . Don ' t
do this if we ' re rolling already .
*/
request_locate ( location - > start ( ) , false ) ;
}
}
} else {
if ( ! change_transport_roll & & Config - > get_seamless_loop ( ) & & transport_rolling ( ) ) {
// request an immediate locate to refresh the tracks
// after disabling looping
2017-09-18 12:39:17 -04:00
request_locate ( _transport_sample - 1 , false ) ;
2014-04-17 09:47:06 -04:00
}
2008-06-02 17:41:35 -04:00
}
}
void
2009-11-08 11:28:21 -05:00
Session : : request_play_range ( list < AudioRange > * range , bool leave_rolling )
{
2009-12-03 21:15:12 -05:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetPlayAudioRange , SessionEvent : : Add , SessionEvent : : Immediate , 0 , ( leave_rolling ? 1.0 : 0.0 ) ) ;
2009-11-08 11:28:21 -05:00
if ( range ) {
ev - > audio_range = * range ;
} else {
ev - > audio_range . clear ( ) ;
}
2009-11-30 11:12:13 -05:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Request play range, leave rolling ? %1 \n " , leave_rolling ) ) ;
2009-11-08 11:28:21 -05:00
queue_event ( ev ) ;
}
2014-07-02 18:34:49 -04:00
void
Session : : request_cancel_play_range ( )
{
SessionEvent * ev = new SessionEvent ( SessionEvent : : CancelPlayAudioRange , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0 ) ;
queue_event ( ev ) ;
}
2018-02-11 10:39:36 -05:00
bool
Session : : solo_selection_active ( )
{
if ( _soloSelection . empty ( ) ) {
return false ;
}
return true ;
}
void
Session : : solo_selection ( StripableList & list , bool new_state )
{
boost : : shared_ptr < ControlList > solo_list ( new ControlList ) ;
boost : : shared_ptr < ControlList > unsolo_list ( new ControlList ) ;
if ( new_state )
_soloSelection = list ;
else
_soloSelection . clear ( ) ;
2018-09-18 18:51:59 -04:00
2018-02-11 10:39:36 -05:00
boost : : shared_ptr < RouteList > rl = get_routes ( ) ;
2018-09-18 18:51:59 -04:00
2018-02-11 10:39:36 -05:00
for ( ARDOUR : : RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
if ( ! ( * i ) - > is_track ( ) ) {
continue ;
}
2018-09-18 18:51:59 -04:00
2018-02-11 10:39:36 -05:00
boost : : shared_ptr < Stripable > s ( * i ) ;
bool found = ( std : : find ( list . begin ( ) , list . end ( ) , s ) ! = list . end ( ) ) ;
if ( new_state & & found ) {
2018-09-18 18:51:59 -04:00
2018-02-11 10:39:36 -05:00
solo_list - > push_back ( s - > solo_control ( ) ) ;
2018-09-18 18:51:59 -04:00
2018-02-11 10:39:36 -05:00
//must invalidate playlists on selected tracks, so only selected regions get heard
boost : : shared_ptr < Track > track = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( track ) {
boost : : shared_ptr < Playlist > playlist = track - > playlist ( ) ;
if ( playlist ) {
playlist - > ContentsChanged ( ) ;
}
}
} else {
unsolo_list - > push_back ( s - > solo_control ( ) ) ;
}
}
set_controls ( solo_list , 1.0 , Controllable : : NoGroup ) ;
set_controls ( unsolo_list , 0.0 , Controllable : : NoGroup ) ;
}
2009-11-08 11:28:21 -05:00
void
Session : : realtime_stop ( bool abort , bool clear_state )
2008-06-02 17:41:35 -04:00
{
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " realtime stop @ %1 \n " , _transport_sample ) ) ;
2009-11-08 11:28:21 -05:00
PostTransportWork todo = PostTransportWork ( 0 ) ;
2008-06-02 17:41:35 -04:00
/* assume that when we start, we'll be moving forwards */
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( _transport_speed < 0.0f ) {
2009-11-08 11:28:21 -05:00
todo = ( PostTransportWork ( todo | PostTransportStop | PostTransportReverse ) ) ;
2013-01-15 20:57:40 -05:00
_default_transport_speed = 1.0 ;
2008-06-02 17:41:35 -04:00
} else {
2009-11-08 11:28:21 -05:00
todo = PostTransportWork ( todo | PostTransportStop ) ;
2008-06-02 17:41:35 -04:00
}
2010-12-07 09:44:47 -05:00
/* call routes */
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
( * i ) - > realtime_handle_transport_stopped ( ) ;
}
2015-10-05 10:17:49 -04:00
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " stop complete, auto-return scheduled for return to %1 \n " , _requested_return_sample ) ) ;
2010-12-07 09:44:47 -05:00
2014-10-10 13:22:45 -04:00
/* the duration change is not guaranteed to have happened, but is likely */
2015-10-05 10:17:49 -04:00
2014-10-10 13:22:45 -04:00
todo = PostTransportWork ( todo | PostTransportDuration ) ;
2008-06-02 17:41:35 -04:00
if ( abort ) {
2009-11-08 11:28:21 -05:00
todo = PostTransportWork ( todo | PostTransportAbort ) ;
}
if ( clear_state ) {
todo = PostTransportWork ( todo | PostTransportClearSubstate ) ;
}
if ( todo ) {
add_post_transport_work ( todo ) ;
2008-06-02 17:41:35 -04:00
}
2009-12-03 21:15:12 -05:00
_clear_event_type ( SessionEvent : : StopOnce ) ;
_clear_event_type ( SessionEvent : : RangeStop ) ;
_clear_event_type ( SessionEvent : : RangeLocate ) ;
2008-06-02 17:41:35 -04:00
2018-02-11 10:39:36 -05:00
//clear our solo-selection, if there is one
if ( solo_selection_active ( ) ) {
solo_selection ( _soloSelection , false ) ;
}
2018-09-18 18:51:59 -04:00
2011-03-14 17:53:10 -04:00
/* if we're going to clear loop state, then force disabling record BUT only if we're not doing latched rec-enable */
2010-04-20 22:24:38 -04:00
disable_record ( true , ( ! Config - > get_latched_record_enable ( ) & & clear_state ) ) ;
2011-03-14 17:53:10 -04:00
2015-09-22 13:20:52 -04:00
if ( clear_state & & ! Config - > get_loop_is_mode ( ) ) {
unset_play_loop ( ) ;
}
2015-10-05 10:17:49 -04:00
2008-06-02 17:41:35 -04:00
reset_slave_state ( ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
_transport_speed = 0 ;
2009-06-09 20:03:47 -04:00
_target_transport_speed = 0 ;
2017-10-29 13:32:17 -04:00
_engine_speed = 1.0 ;
2008-06-02 17:41:35 -04:00
2010-05-11 20:29:28 -04:00
g_atomic_int_set ( & _playback_load , 100 ) ;
g_atomic_int_set ( & _capture_load , 100 ) ;
2009-05-15 21:53:43 -04:00
if ( config . get_use_video_sync ( ) ) {
2008-06-02 17:41:35 -04:00
waiting_for_sync_offset = true ;
}
2010-07-08 20:57:58 -04:00
transport_sub_state = 0 ;
2008-06-02 17:41:35 -04:00
}
2011-05-30 17:37:58 -04:00
void
Session : : realtime_locate ( )
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
( * i ) - > realtime_locate ( ) ;
}
}
2008-06-02 17:41:35 -04:00
void
Session : : butler_transport_work ( )
{
2015-09-30 13:02:50 -04:00
/* Note: this function executes in the butler thread context */
2008-06-02 17:41:35 -04:00
restart :
bool finished ;
2009-11-08 11:28:21 -05:00
PostTransportWork ptw ;
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2015-01-29 18:59:40 -05:00
uint64_t before ;
2015-10-05 10:17:49 -04:00
2009-10-23 20:39:28 -04:00
int on_entry = g_atomic_int_get ( & _butler - > should_do_transport_work ) ;
2008-06-02 17:41:35 -04:00
finished = true ;
2009-11-08 11:28:21 -05:00
ptw = post_transport_work ( ) ;
2009-11-30 11:12:13 -05:00
2015-01-29 18:59:40 -05:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Butler transport work, todo = %1 at %2 \n " , enum_2_string ( ptw ) , ( before = g_get_monotonic_time ( ) ) ) ) ;
2010-06-09 13:24:07 -04:00
2015-09-28 17:42:02 -04:00
if ( ptw & PostTransportLocate ) {
2015-09-30 13:02:50 -04:00
if ( get_play_loop ( ) & & ! Config - > get_seamless_loop ( ) ) {
DEBUG_TRACE ( DEBUG : : Butler , " flush loop recording fragment to disk \n " ) ;
2015-10-05 10:17:49 -04:00
2015-09-30 13:02:50 -04:00
/* this locate might be happening while we are
2015-10-04 14:51:05 -04:00
* loop recording .
2015-09-30 13:02:50 -04:00
*
* Non - seamless looping will require a locate ( below ) that
* will reset capture buffers and throw away data .
*
2015-10-04 14:51:05 -04:00
* Rather than first find all tracks and see if they
2015-09-30 13:02:50 -04:00
* have outstanding data , just do a flush anyway . It
* may be cheaper this way anyway , and is certainly
* more accurate .
2015-09-28 17:42:02 -04:00
*/
bool more_disk_io_to_do = false ;
uint32_t errors = 0 ;
do {
2015-09-30 13:02:50 -04:00
more_disk_io_to_do = _butler - > flush_tracks_to_disk_after_locate ( r , errors ) ;
2015-09-28 17:42:02 -04:00
if ( errors ) {
break ;
}
if ( more_disk_io_to_do ) {
continue ;
}
} while ( false ) ;
}
}
2011-03-14 17:53:10 -04:00
if ( ptw & PostTransportAdjustPlaybackBuffering ) {
2010-06-09 13:24:07 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr ) {
tr - > adjust_playback_buffering ( ) ;
2011-03-14 17:53:10 -04:00
/* and refill those buffers ... */
2010-06-09 13:24:07 -04:00
}
2017-09-18 12:39:17 -04:00
( * i ) - > non_realtime_locate ( _transport_sample ) ;
2010-06-09 13:24:07 -04:00
}
2017-07-21 17:07:51 -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 ) - > non_realtime_locate ( _transport_sample ) ;
2017-07-21 17:07:51 -04:00
}
2011-03-14 17:53:10 -04:00
}
if ( ptw & PostTransportAdjustCaptureBuffering ) {
2018-11-28 15:41:08 -05:00
/* need to prevent concurrency with ARDOUR::DiskWriter::run(),
* DiskWriter : : adjust_buffering ( ) re - allocates the ringbuffer */
Glib : : Threads : : Mutex : : Lock lx ( AudioEngine : : instance ( ) - > process_lock ( ) ) ;
2010-06-09 13:24:07 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr ) {
tr - > adjust_capture_buffering ( ) ;
}
}
2011-03-14 17:53:10 -04:00
}
2010-06-09 13:24:07 -04:00
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportReverse ) {
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
clear_clicks ( ) ;
/* don't seek if locate will take care of that in non_realtime_stop() */
2009-11-08 11:28:21 -05:00
if ( ! ( ptw & PostTransportLocate ) ) {
2010-04-21 16:42:22 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
( * i ) - > non_realtime_locate ( _transport_sample ) ;
2012-07-13 17:05:45 -04:00
2009-10-23 20:39:28 -04:00
if ( on_entry ! = g_atomic_int_get ( & _butler - > should_do_transport_work ) ) {
2008-06-02 17:41:35 -04:00
/* new request, stop seeking, and start again */
2009-10-23 20:39:28 -04:00
g_atomic_int_dec_and_test ( & _butler - > should_do_transport_work ) ;
2008-06-02 17:41:35 -04:00
goto restart ;
}
}
2017-07-21 17:07:51 -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 ) - > non_realtime_locate ( _transport_sample ) ;
2017-07-21 17:07:51 -04:00
}
2008-06-02 17:41:35 -04:00
}
}
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportLocate ) {
2015-02-13 15:27:53 -05:00
DEBUG_TRACE ( DEBUG : : Transport , " nonrealtime locate invoked from BTW \n " ) ;
2008-06-02 17:41:35 -04:00
non_realtime_locate ( ) ;
}
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportStop ) {
non_realtime_stop ( ptw & PostTransportAbort , on_entry , finished ) ;
2008-06-02 17:41:35 -04:00
if ( ! finished ) {
2009-10-23 20:39:28 -04:00
g_atomic_int_dec_and_test ( & _butler - > should_do_transport_work ) ;
2008-06-02 17:41:35 -04:00
goto restart ;
}
}
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportOverWrite ) {
2008-06-02 17:41:35 -04:00
non_realtime_overwrite ( on_entry , finished ) ;
if ( ! finished ) {
2009-10-23 20:39:28 -04:00
g_atomic_int_dec_and_test ( & _butler - > should_do_transport_work ) ;
2008-06-02 17:41:35 -04:00
goto restart ;
}
}
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportAudition ) {
2008-06-02 17:41:35 -04:00
non_realtime_set_audition ( ) ;
}
2008-08-04 18:37:24 -04:00
2009-10-23 20:39:28 -04:00
g_atomic_int_dec_and_test ( & _butler - > should_do_transport_work ) ;
2010-04-19 13:48:11 -04:00
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( X_ ( " Butler transport work all done after %1 usecs @ %2 trw = %3 \n " ) , g_get_monotonic_time ( ) - before , _transport_sample , _butler - > transport_work_requested ( ) ) ) ;
2008-06-02 17:41:35 -04:00
}
void
Session : : non_realtime_overwrite ( int on_entry , bool & finished )
{
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr & & tr - > pending_overwrite ( ) ) {
tr - > overwrite_existing_buffers ( ) ;
2008-06-02 17:41:35 -04:00
}
2009-10-23 20:39:28 -04:00
if ( on_entry ! = g_atomic_int_get ( & _butler - > should_do_transport_work ) ) {
2008-06-02 17:41:35 -04:00
finished = false ;
return ;
}
}
}
2019-02-20 19:34:58 -05:00
bool
Session : : declick_in_progress ( ) const
{
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
2019-02-21 10:10:45 -05:00
if ( ( * i ) - > declick_in_progress ( ) ) {
2019-02-20 19:34:58 -05:00
return true ;
}
}
return false ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
void
Session : : non_realtime_locate ( )
{
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " locate tracks to %1 \n " , _transport_sample ) ) ;
2015-02-13 15:27:53 -05:00
if ( Config - > get_loop_is_mode ( ) & & get_play_loop ( ) ) {
2015-02-16 19:30:21 -05:00
2015-02-13 15:27:53 -05:00
Location * loc = _locations - > auto_loop_location ( ) ;
2015-02-16 19:30:21 -05:00
2017-09-18 12:39:17 -04:00
if ( ! loc | | ( _transport_sample < loc - > start ( ) | | _transport_sample > = loc - > end ( ) ) ) {
2015-02-13 15:27:53 -05:00
/* jumped out of loop range: stop tracks from looping,
but leave loop ( mode ) enabled .
*/
set_track_loop ( false ) ;
2015-02-16 19:30:21 -05:00
2015-02-17 07:20:42 -05:00
} else if ( loc & & Config - > get_seamless_loop ( ) & &
2017-09-18 12:39:17 -04:00
( ( loc - > start ( ) < = _transport_sample ) | |
( loc - > end ( ) > _transport_sample ) ) ) {
2015-02-16 19:30:21 -05:00
/* jumping to start of loop. This might have been done before but it is
* idempotent and cheap . Doing it here ensures that when we start playback
* outside the loop we still flip tracks into the magic seamless mode
* when needed .
*/
set_track_loop ( true ) ;
} else if ( loc ) {
set_track_loop ( false ) ;
2015-02-13 15:27:53 -05:00
}
2015-10-05 10:17:49 -04:00
2015-02-16 19:30:21 -05:00
} else {
/* no more looping .. should have been noticed elsewhere */
2015-02-13 15:27:53 -05:00
}
2015-02-16 19:30:21 -05:00
2015-10-05 10:17:49 -04:00
2017-09-18 12:39:17 -04:00
samplepos_t tf ;
2017-07-28 13:37:11 -04:00
2016-05-07 11:16:59 -04:00
{
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
2017-07-04 12:14:33 -04:00
2017-08-01 11:13:35 -04:00
restart :
gint sc = g_atomic_int_get ( & _seek_counter ) ;
2017-09-18 12:39:17 -04:00
tf = _transport_sample ;
2017-07-04 12:14:33 -04:00
2016-05-07 11:16:59 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
2017-07-28 13:37:11 -04:00
( * i ) - > non_realtime_locate ( tf ) ;
2017-08-01 11:13:35 -04:00
if ( sc ! = g_atomic_int_get ( & _seek_counter ) ) {
goto restart ;
}
2016-05-07 11:16:59 -04:00
}
2008-06-02 17:41:35 -04:00
}
2017-07-17 14:04:31 -04:00
2017-07-21 17:07:51 -04:00
{
2017-08-01 11:13:35 -04:00
/* VCAs are quick to locate because they have no data (except
automation ) associated with them . Don ' t bother with a
2017-09-18 12:39:17 -04:00
restart mechanism here , but do use the same transport sample
2017-08-01 11:13:35 -04:00
that the Routes used .
*/
2017-07-21 17:07:51 -04:00
VCAList v = _vca_manager - > vcas ( ) ;
for ( VCAList : : const_iterator i = v . begin ( ) ; i ! = v . end ( ) ; + + i ) {
2017-07-28 13:37:11 -04:00
( * i ) - > non_realtime_locate ( tf ) ;
2017-07-21 17:07:51 -04:00
}
}
2011-09-14 09:46:34 -04:00
2017-09-18 12:39:17 -04:00
_scene_changer - > locate ( _transport_sample ) ;
2014-05-02 18:43:37 -04:00
2011-09-14 09:46:34 -04:00
/* XXX: it would be nice to generate the new clicks here (in the non-RT thread)
rather than clearing them so that the RT thread has to spend time constructing
them ( in Session : : click ) .
*/
clear_clicks ( ) ;
2008-06-02 17:41:35 -04:00
}
2015-06-25 13:12:47 -04:00
# ifdef USE_TRACKS_CODE_FEATURES
2015-01-16 12:17:09 -05:00
bool
2017-09-18 12:39:17 -04:00
Session : : select_playhead_priority_target ( samplepos_t & jump_to )
2015-01-16 12:17:09 -05:00
{
jump_to = - 1 ;
AutoReturnTarget autoreturn = Config - > get_auto_return_target_list ( ) ;
2015-01-16 17:50:10 -05:00
2015-01-16 12:17:09 -05:00
if ( ! autoreturn ) {
return false ;
}
2015-05-12 23:17:14 -04:00
if ( Profile - > get_trx ( ) & & transport_rolling ( ) ) {
// We're playing, so do nothing.
// Next stop will put us where we need to be.
return false ;
}
2015-10-05 10:17:49 -04:00
2015-01-16 12:17:09 -05:00
/* Note that the order of checking each AutoReturnTarget flag defines
the priority each flag .
2015-05-12 23:17:14 -04:00
Ardour / Mixbus : Last Locate
Range Selection
Loop Range
Region Selection
Tracks : Range Selection
Loop Range
Region Selection
Last Locate
2015-01-16 12:17:09 -05:00
*/
2015-10-05 10:17:49 -04:00
2015-05-12 23:17:14 -04:00
if ( autoreturn & RangeSelectionStart ) {
2015-01-16 12:17:09 -05:00
if ( ! _range_selection . empty ( ) ) {
jump_to = _range_selection . from ;
2015-05-12 23:17:14 -04:00
} else {
2015-05-13 00:31:24 -04:00
if ( transport_rolling ( ) ) {
/* Range selection no longer exists, but we're playing,
so do nothing . Next stop will put us where
we need to be .
*/
return false ;
2015-05-12 23:17:14 -04:00
}
2015-01-16 12:17:09 -05:00
}
}
2015-10-05 10:17:49 -04:00
2015-05-19 09:40:04 -04:00
if ( jump_to < 0 & & ( autoreturn & Loop ) & & get_play_loop ( ) ) {
2015-01-16 12:17:09 -05:00
/* don't try to handle loop play when synced to JACK */
2015-10-05 10:17:49 -04:00
2015-01-16 12:17:09 -05:00
if ( ! synced_to_engine ( ) ) {
Location * location = _locations - > auto_loop_location ( ) ;
2015-10-05 10:17:49 -04:00
2015-01-16 12:17:09 -05:00
if ( location ) {
jump_to = location - > start ( ) ;
2015-05-12 23:17:14 -04:00
if ( Config - > get_seamless_loop ( ) ) {
/* need to get track buffers reloaded */
set_track_loop ( true ) ;
}
2015-10-04 14:51:05 -04:00
}
2015-01-16 12:17:09 -05:00
}
}
2015-10-05 10:17:49 -04:00
2015-01-16 12:17:09 -05:00
if ( jump_to < 0 & & ( autoreturn & RegionSelectionStart ) ) {
if ( ! _object_selection . empty ( ) ) {
jump_to = _object_selection . from ;
}
2015-10-04 14:51:05 -04:00
}
2015-01-16 12:17:09 -05:00
2015-05-12 23:17:14 -04:00
if ( jump_to < 0 & & ( autoreturn & LastLocate ) ) {
jump_to = _last_roll_location ;
}
2015-10-05 10:17:49 -04:00
2015-01-16 12:17:09 -05:00
return jump_to > = 0 ;
}
2015-06-25 13:12:47 -04:00
# else
bool
2017-09-18 12:39:17 -04:00
Session : : select_playhead_priority_target ( samplepos_t & jump_to )
2015-06-25 13:12:47 -04:00
{
2019-02-28 19:18:08 -05:00
if ( ! transport_master_no_external_or_using_engine ( ) | | ! config . get_auto_return ( ) ) {
2015-06-25 13:12:47 -04:00
return false ;
}
jump_to = _last_roll_location ;
return jump_to > = 0 ;
}
# endif
2015-01-16 12:17:09 -05:00
void
Session : : follow_playhead_priority ( )
{
2017-09-18 12:39:17 -04:00
samplepos_t target ;
2015-01-16 12:17:09 -05:00
if ( select_playhead_priority_target ( target ) ) {
request_locate ( target ) ;
}
}
2008-06-02 17:41:35 -04:00
void
Session : : non_realtime_stop ( bool abort , int on_entry , bool & finished )
{
struct tm * now ;
time_t xnow ;
bool did_record ;
bool saved ;
2009-11-08 11:28:21 -05:00
PostTransportWork ptw = post_transport_work ( ) ;
2008-06-02 17:41:35 -04:00
did_record = false ;
saved = false ;
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
2017-09-18 12:39:17 -04:00
if ( tr & & tr - > get_captured_samples ( ) ! = 0 ) {
2008-06-02 17:41:35 -04:00
did_record = true ;
break ;
}
}
/* stop and locate are merged here because they share a lot of common stuff */
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
time ( & xnow ) ;
now = localtime ( & xnow ) ;
if ( auditioner ) {
auditioner - > cancel_audition ( ) ;
}
if ( did_record ) {
2011-01-19 12:38:56 -05:00
begin_reversible_command ( Operations : : capture ) ;
2008-06-02 17:41:35 -04:00
_have_captured = true ;
}
2010-04-19 13:48:11 -04:00
DEBUG_TRACE ( DEBUG : : Transport , X_ ( " Butler PTW: DS stop \n " ) ) ;
2010-07-16 10:55:11 -04:00
2011-03-14 17:53:10 -04:00
if ( abort & & did_record ) {
/* no reason to save the session file when we remove sources
*/
_state_of_the_state = StateOfTheState ( _state_of_the_state | InCleanup ) ;
}
2010-07-16 10:55:11 -04:00
2010-04-21 16:42:22 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr ) {
tr - > transport_stopped_wallclock ( * now , xnow , abort ) ;
}
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2011-03-14 17:53:10 -04:00
if ( abort & & did_record ) {
2011-06-01 12:50:12 -04:00
_state_of_the_state = StateOfTheState ( _state_of_the_state & ~ InCleanup ) ;
2011-03-14 17:53:10 -04:00
}
2010-07-16 10:55:11 -04:00
2008-06-02 17:41:35 -04:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
if ( did_record ) {
commit_reversible_command ( ) ;
2014-06-26 13:07:38 -04:00
/* increase take name */
if ( config . get_track_name_take ( ) & & ! config . get_take_name ( ) . empty ( ) ) {
string newname = config . get_take_name ( ) ;
config . set_take_name ( bump_name_number ( newname ) ) ;
}
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
if ( _engine . running ( ) ) {
2011-03-14 17:53:10 -04:00
PostTransportWork ptw = post_transport_work ( ) ;
2017-07-21 15:46:36 -04:00
2011-03-14 17:53:10 -04:00
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2018-05-22 12:09:26 -04:00
( * i ) - > non_realtime_transport_stop ( _transport_sample , ! ( ptw & PostTransportLocate ) ) ;
2011-03-14 16:33:47 -04:00
}
2017-07-21 15:46:36 -04:00
VCAList v = _vca_manager - > vcas ( ) ;
for ( VCAList : : const_iterator i = v . begin ( ) ; i ! = v . end ( ) ; + + i ) {
2018-05-22 12:09:26 -04:00
( * i ) - > non_realtime_transport_stop ( _transport_sample , ! ( ptw & PostTransportLocate ) ) ;
2017-07-21 15:46:36 -04:00
}
2011-03-14 16:33:47 -04:00
update_latency_compensation ( ) ;
2008-06-02 17:41:35 -04:00
}
2019-02-28 19:18:08 -05:00
/* If we are not synced to a "true" external master, and we're not
* handling an explicit locate , we should consider whether or not to
* " auto-return " . This could mean going to a specifically requested
* location , or just back to the start of the last roll .
*/
2009-10-14 12:10:01 -04:00
2019-02-28 19:18:08 -05:00
if ( transport_master_no_external_or_using_engine ( ) & & ! ( ptw & PostTransportLocate ) ) {
2008-08-04 18:37:24 -04:00
2019-02-28 19:18:08 -05:00
bool do_locate = false ;
2019-02-26 12:17:04 -05:00
2019-02-28 19:18:08 -05:00
if ( _requested_return_sample > = 0 ) {
2008-06-02 17:41:35 -04:00
2019-02-28 19:18:08 -05:00
/* explicit return request pre-queued in event list. overrides everything else */
2009-10-30 14:14:25 -04:00
2019-02-28 19:18:08 -05:00
_transport_sample = _requested_return_sample ;
2008-06-02 17:41:35 -04:00
2019-02-28 19:18:08 -05:00
/* cancel this request */
_requested_return_sample = - 1 ;
do_locate = true ;
2009-10-30 14:14:25 -04:00
2019-02-28 19:18:08 -05:00
} else if ( Config - > get_auto_return_target_list ( ) ) {
2011-06-01 12:50:12 -04:00
2019-02-28 19:18:08 -05:00
samplepos_t jump_to ;
2009-10-30 14:14:25 -04:00
2019-02-28 19:18:08 -05:00
if ( select_playhead_priority_target ( jump_to ) ) {
2009-10-30 14:14:25 -04:00
2019-02-28 19:18:08 -05:00
/* there's a valid target (we don't care how it
* was derived here )
*/
2011-06-01 12:50:12 -04:00
2019-02-28 19:18:08 -05:00
_transport_sample = jump_to ;
do_locate = true ;
2011-06-01 12:50:12 -04:00
2019-02-28 19:18:08 -05:00
} else if ( abort ) {
2014-08-02 07:26:04 -04:00
2019-02-28 19:18:08 -05:00
/* roll aborted (typically capture) with
* auto - return enabled
*/
2008-08-04 18:37:24 -04:00
2019-02-28 19:18:08 -05:00
_transport_sample = _last_roll_location ;
do_locate = true ;
2009-10-30 14:14:25 -04:00
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
}
2008-06-02 17:41:35 -04:00
2019-02-28 19:18:08 -05:00
if ( do_locate & & synced_to_engine ( ) ) {
/* We will unconditionally locate to _transport_sample
* below , which will refill playback buffers based on
* _transport_sample , and maximises the buffering they
* represent .
*
* But if we are synced to engine ( JACK ) , we should
* locate the engine ( JACK ) as well . We would follow
* the engine ( JACK ) on the next process cycle , but
* since we ' re going to do a locate below anyway ,
* it seems pointless to not use just do it ourselves
* right now , rather than wait for the engine ( JACK ) to
* provide the new position on the next cycle .
*
* Despite the generic name of the called method
* ( : : transport_locate ( ) ) this method only does
* anything if the audio / MIDI backend is JACK .
*/
_engine . transport_locate ( _transport_sample ) ;
}
2009-10-28 19:56:05 -04:00
}
2012-01-23 21:30:46 -05:00
clear_clicks ( ) ;
2017-01-19 07:03:57 -05:00
unset_preroll_record_trim ( ) ;
2012-01-23 21:30:46 -05:00
2010-04-21 16:42:22 -04:00
/* do this before seeking, because otherwise the tracks will do the wrong thing in seamless loop mode.
2009-11-08 11:28:21 -05:00
*/
if ( ptw & PostTransportClearSubstate ) {
2014-04-17 09:47:06 -04:00
unset_play_range ( ) ;
if ( ! Config - > get_loop_is_mode ( ) ) {
unset_play_loop ( ) ;
}
2009-11-08 11:28:21 -05:00
}
2009-10-30 14:14:25 -04:00
/* this for() block can be put inside the previous if() and has the effect of ... ??? what */
2016-05-07 11:16:59 -04:00
{
DEBUG_TRACE ( DEBUG : : Transport , X_ ( " Butler PTW: locate \n " ) ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " Butler PTW: locate on %1 \n " , ( * i ) - > name ( ) ) ) ;
2017-09-18 12:39:17 -04:00
( * i ) - > non_realtime_locate ( _transport_sample ) ;
2010-04-19 13:48:11 -04:00
2016-05-07 11:16:59 -04:00
if ( on_entry ! = g_atomic_int_get ( & _butler - > should_do_transport_work ) ) {
finished = false ;
/* we will be back */
return ;
}
2008-06-02 17:41:35 -04:00
}
2009-10-28 19:53:16 -04:00
}
2008-06-02 17:41:35 -04:00
2017-07-21 17:07:51 -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 ) - > non_realtime_locate ( _transport_sample ) ;
2017-07-21 17:07:51 -04:00
}
}
2009-10-28 19:56:05 -04:00
have_looped = false ;
2008-12-12 09:43:24 -05:00
2013-08-09 18:06:08 -04:00
/* don't bother with this stuff if we're disconnected from the engine,
because there will be no process callbacks to deliver stuff from
*/
2018-11-28 09:24:47 -05:00
if ( _engine . running ( ) & & ! _engine . freewheeling ( ) ) {
2013-08-07 22:22:11 -04:00
// need to queue this in the next RT cycle
_send_timecode_update = true ;
2015-10-05 10:17:49 -04:00
2018-09-18 18:51:59 -04:00
if ( transport_master ( ) - > type ( ) = = MTC ) {
2014-10-22 17:06:53 -04:00
send_immediate_mmc ( MIDI : : MachineControlCommand ( MIDI : : MachineControl : : cmdStop ) ) ;
2013-08-09 18:06:08 -04:00
/* This (::non_realtime_stop()) gets called by main
process thread , which will lead to confusion
when calling AsyncMIDIPort : : write ( ) .
2015-10-05 10:17:49 -04:00
2013-08-09 18:06:08 -04:00
Something must be done . XXX
*/
2017-09-18 12:39:17 -04:00
send_mmc_locate ( _transport_sample ) ;
2012-02-06 12:09:53 -05:00
}
2011-03-14 17:53:10 -04:00
}
2008-12-12 09:43:24 -05:00
2009-11-08 11:28:21 -05:00
if ( ( ptw & PostTransportLocate ) & & get_record_enabled ( ) ) {
2012-10-16 16:53:08 -04:00
/* This is scheduled by realtime_stop(), which is also done
* when a slave requests / locate / for an initial sync .
* We can ' t hold up the slave for long with a save ( ) here ,
* without breaking its initial sync cycle .
*
* save state only if there ' s no slave or if it ' s not yet locked .
*/
2018-09-18 18:51:59 -04:00
if ( ! transport_master_is_external ( ) | | ! transport_master ( ) - > locked ( ) ) {
2015-01-24 19:27:28 -05:00
DEBUG_TRACE ( DEBUG : : Transport , X_ ( " Butler PTW: requests save \n " ) ) ;
SaveSessionRequested ( _current_snapshot_name ) ;
2012-10-16 16:53:08 -04:00
saved = true ;
}
2008-06-02 17:41:35 -04:00
}
2011-03-14 17:53:10 -04:00
/* always try to get rid of this */
2008-06-02 17:41:35 -04:00
2011-03-14 17:53:10 -04:00
remove_pending_capture_state ( ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
/* save the current state of things if appropriate */
if ( did_record & & ! saved ) {
2015-01-24 19:27:28 -05:00
SaveSessionRequested ( _current_snapshot_name ) ;
2008-06-02 17:41:35 -04:00
}
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportStop ) {
2014-04-17 09:47:06 -04:00
unset_play_range ( ) ;
if ( ! Config - > get_loop_is_mode ( ) ) {
unset_play_loop ( ) ;
}
2008-06-02 17:41:35 -04:00
}
2017-09-18 12:39:17 -04:00
PositionChanged ( _transport_sample ) ; /* EMIT SIGNAL */
2012-07-16 23:10:40 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " send TSC with speed = %1 \n " , _transport_speed ) ) ;
2008-06-02 17:41:35 -04:00
TransportStateChange ( ) ; /* EMIT SIGNAL */
2017-09-18 12:39:17 -04:00
AutomationWatch : : instance ( ) . transport_stop_automation_watches ( _transport_sample ) ;
2008-06-02 17:41:35 -04:00
/* and start it up again if relevant */
2018-05-22 12:09:26 -04:00
if ( ( ptw & PostTransportLocate ) & & ! config . get_external_sync ( ) ) {
2008-06-02 17:41:35 -04:00
request_transport_speed ( 1.0 ) ;
}
}
void
2009-11-08 11:28:21 -05:00
Session : : unset_play_loop ( )
{
2015-02-19 10:33:20 -05:00
if ( play_loop ) {
play_loop = false ;
clear_events ( SessionEvent : : AutoLoop ) ;
set_track_loop ( false ) ;
2015-10-05 10:17:49 -04:00
2015-02-19 10:33:20 -05:00
if ( Config - > get_seamless_loop ( ) ) {
/* likely need to flush track buffers: this will locate us to wherever we are */
add_post_transport_work ( PostTransportLocate ) ;
_butler - > schedule_transport_work ( ) ;
}
2018-10-05 15:48:41 -04:00
TransportStateChange ( ) ; /* EMIT SIGNAL */
2015-02-16 19:30:21 -05:00
}
2015-02-13 15:27:53 -05:00
}
void
Session : : set_track_loop ( bool yn )
{
Location * loc = _locations - > auto_loop_location ( ) ;
2011-06-01 12:50:12 -04:00
2015-02-13 15:27:53 -05:00
if ( ! loc ) {
yn = false ;
}
2015-02-16 19:30:21 -05:00
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
2015-02-16 19:30:21 -05:00
2010-04-21 16:42:22 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
2017-10-31 13:32:26 -04:00
if ( * i & & ! ( * i ) - > is_private_route ( ) ) {
( * i ) - > set_loop ( yn ? loc : 0 ) ;
2009-11-08 11:28:21 -05:00
}
}
}
void
2014-04-17 09:47:06 -04:00
Session : : set_play_loop ( bool yn , double speed )
2008-06-02 17:41:35 -04:00
{
/* Called from event-handling context */
2008-08-04 18:37:24 -04:00
2009-10-30 14:14:25 -04:00
Location * loc ;
2010-08-09 12:40:31 -04:00
if ( yn = = play_loop | | ( actively_recording ( ) & & yn ) | | ( loc = _locations - > auto_loop_location ( ) ) = = 0 ) {
2009-11-08 11:28:21 -05:00
/* nothing to do, or can't change loop status while recording */
2008-06-02 17:41:35 -04:00
return ;
}
2011-06-01 12:50:12 -04:00
2013-09-19 17:34:23 -04:00
if ( yn & & Config - > get_seamless_loop ( ) & & synced_to_engine ( ) ) {
2011-03-14 17:53:10 -04:00
warning < < string_compose (
_ ( " Seamless looping cannot be supported while %1 is using JACK transport. \n "
" Recommend changing the configured options " ) , PROGRAM_NAME )
2008-06-02 17:41:35 -04:00
< < endmsg ;
return ;
}
2011-06-01 12:50:12 -04:00
2009-11-08 11:28:21 -05:00
if ( yn ) {
2008-06-02 17:41:35 -04:00
2009-11-08 11:28:21 -05:00
play_loop = true ;
2015-02-16 19:30:21 -05:00
have_looped = false ;
2015-10-05 10:17:49 -04:00
2009-10-30 14:14:25 -04:00
if ( loc ) {
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
unset_play_range ( ) ;
2008-06-02 17:41:35 -04:00
if ( Config - > get_seamless_loop ( ) ) {
2015-02-16 19:30:21 -05:00
if ( ! Config - > get_loop_is_mode ( ) ) {
/* set all tracks to use internal looping */
set_track_loop ( true ) ;
} else {
2015-10-04 14:51:05 -04:00
/* we will do this in the locate to the start OR when we hit the end
* of the loop for the first time
2015-02-16 19:30:21 -05:00
*/
}
2015-02-13 15:27:53 -05:00
} else {
2015-02-16 19:30:21 -05:00
/* set all tracks to NOT use internal looping */
2015-02-13 15:27:53 -05:00
set_track_loop ( false ) ;
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
2012-06-20 14:46:05 -04:00
/* Put the delick and loop events in into the event list. The declick event will
cause a de - clicking fade - out just before the end of the loop , and it will also result
in a fade - in when the loop restarts . The AutoLoop event will peform the actual loop .
*/
2011-06-01 12:50:12 -04:00
2017-09-18 12:39:17 -04:00
samplepos_t dcp ;
samplecnt_t dcl ;
2012-06-20 14:46:05 -04:00
auto_loop_declick_range ( loc , dcp , dcl ) ;
merge_event ( new SessionEvent ( SessionEvent : : AutoLoop , SessionEvent : : Replace , loc - > end ( ) , loc - > start ( ) , 0.0f ) ) ;
2008-06-02 17:41:35 -04:00
2014-04-17 09:47:06 -04:00
/* if requested to roll, locate to start of loop and
* roll but ONLY if we ' re not already rolling .
2012-06-22 10:27:51 -04:00
args : positition , roll = true , flush = true , with_loop = false , force buffer refill if seamless looping
2009-11-08 11:28:21 -05:00
*/
2008-06-02 17:41:35 -04:00
2014-04-17 09:47:06 -04:00
if ( Config - > get_loop_is_mode ( ) ) {
/* loop IS a transport mode: if already
rolling , do not locate to loop start .
*/
if ( ! transport_rolling ( ) & & ( speed ! = 0.0 ) ) {
2015-09-24 13:40:55 -04:00
start_locate ( loc - > start ( ) , true , true , false , true ) ;
2014-04-17 09:47:06 -04:00
}
} else {
if ( speed ! = 0.0 ) {
2015-09-24 13:40:55 -04:00
start_locate ( loc - > start ( ) , true , true , false , true ) ;
2014-04-17 09:47:06 -04:00
}
}
2009-11-08 11:28:21 -05:00
}
2008-06-02 17:41:35 -04:00
} else {
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
unset_play_loop ( ) ;
2008-06-02 17:41:35 -04:00
}
2012-07-16 23:10:40 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " send TSC2 with speed = %1 \n " , _transport_speed ) ) ;
2009-11-08 11:28:21 -05:00
TransportStateChange ( ) ;
}
2008-06-02 17:41:35 -04:00
void
Session : : flush_all_inserts ( )
{
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
( * i ) - > flush_processors ( ) ;
}
}
void
2017-09-18 12:39:17 -04:00
Session : : start_locate ( samplepos_t target_sample , bool with_roll , bool with_flush , bool for_loop_enabled , bool force )
2008-06-02 17:41:35 -04:00
{
2017-09-18 12:39:17 -04:00
if ( target_sample < 0 ) {
2014-10-22 12:18:31 -04:00
error < < _ ( " Locate called for negative sample position - ignored " ) < < endmsg ;
return ;
}
2013-09-19 17:34:23 -04:00
if ( synced_to_engine ( ) ) {
2008-06-02 17:41:35 -04:00
2009-01-09 04:18:24 -05:00
double sp ;
2017-09-18 12:39:17 -04:00
samplepos_t pos ;
2018-09-25 17:46:59 -04:00
samplepos_t ignore1 , ignore2 ;
2008-06-02 17:41:35 -04:00
2018-09-25 17:46:59 -04:00
transport_master ( ) - > speed_and_position ( sp , pos , ignore1 , ignore2 , 0 ) ;
2008-06-02 17:41:35 -04:00
2017-09-18 12:39:17 -04:00
if ( target_sample ! = pos ) {
2008-06-02 17:41:35 -04:00
2012-07-07 23:40:15 -04:00
if ( config . get_jack_time_master ( ) ) {
/* actually locate now, since otherwise jack_timebase_callback
2017-09-18 12:39:17 -04:00
will use the incorrect _transport_sample and report an old
2012-07-07 23:40:15 -04:00
and incorrect time to Jack transport
*/
2017-09-18 12:39:17 -04:00
locate ( target_sample , with_roll , with_flush , for_loop_enabled , force ) ;
2012-07-07 23:40:15 -04:00
}
2008-06-02 17:41:35 -04:00
/* tell JACK to change transport position, and we will
follow along later in : : follow_slave ( )
*/
2017-09-18 12:39:17 -04:00
_engine . transport_locate ( target_sample ) ;
2008-06-02 17:41:35 -04:00
if ( sp ! = 1.0f & & with_roll ) {
_engine . transport_start ( ) ;
}
}
} else {
2017-09-18 12:39:17 -04:00
locate ( target_sample , with_roll , with_flush , for_loop_enabled , force ) ;
2008-06-02 17:41:35 -04:00
}
}
2017-09-28 00:31:12 -04:00
samplecnt_t
Session : : worst_latency_preroll ( ) const
{
return _worst_output_latency + _worst_input_latency ;
}
2008-09-10 11:03:30 -04:00
int
2017-09-18 12:39:17 -04:00
Session : : micro_locate ( samplecnt_t distance )
2008-09-10 11:03:30 -04:00
{
2010-04-21 16:42:22 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr & & ! tr - > can_internal_playback_seek ( distance ) ) {
2008-09-10 11:03:30 -04:00
return - 1 ;
}
}
2018-09-18 18:51:59 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " micro-locate by %1 \n " , distance ) ) ;
2010-04-21 16:42:22 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr ) {
tr - > internal_playback_seek ( distance ) ;
}
2008-09-10 11:03:30 -04:00
}
2009-10-14 12:10:01 -04:00
2017-09-18 12:39:17 -04:00
_transport_sample + = distance ;
2008-09-10 11:03:30 -04:00
return 0 ;
}
2010-06-28 13:25:40 -04:00
/** @param with_mmc true to send a MMC locate command when the locate is done */
2008-06-02 17:41:35 -04:00
void
2017-09-18 12:39:17 -04:00
Session : : locate ( samplepos_t target_sample , bool with_roll , bool with_flush , bool for_loop_enabled , bool force , bool with_mmc )
2008-06-02 17:41:35 -04:00
{
2015-02-17 11:21:41 -05:00
bool need_butler = false ;
2015-10-05 10:17:49 -04:00
2012-06-22 10:27:51 -04:00
/* Locates for seamless looping are fairly different from other
* locates . They assume that the diskstream buffers for each track
* already have the correct data in them , and thus there is no need to
* actually tell the tracks to locate . What does need to be done ,
* though , is all the housekeeping that is associated with non - linear
2017-09-18 12:39:17 -04:00
* changes in the value of _transport_sample .
2012-06-22 10:27:51 -04:00
*/
2012-09-01 07:24:17 -04:00
2015-09-24 13:40:55 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " rt-locate to %1, roll %2 flush %3 loop-enabled %4 force %5 mmc %6 \n " ,
2017-09-18 12:39:17 -04:00
target_sample , with_roll , with_flush , for_loop_enabled , force , with_mmc ) ) ;
2015-10-05 10:17:49 -04:00
2017-09-18 12:39:17 -04:00
if ( ! force & & _transport_sample = = target_sample & & ! loop_changing & & ! for_loop_enabled ) {
2015-09-28 17:42:02 -04:00
/* already at the desired position. Not forced to locate,
the loop isn ' t changing , so unless we ' re told to
start rolling also , there ' s nothing to do but
tell the world where we are ( again ) .
*/
2008-06-02 17:41:35 -04:00
if ( with_roll ) {
2014-10-10 13:22:45 -04:00
set_transport_speed ( 1.0 , 0 , false ) ;
2008-06-02 17:41:35 -04:00
}
loop_changing = false ;
2010-07-04 21:13:18 -04:00
Located ( ) ; /* EMIT SIGNAL */
2008-06-02 17:41:35 -04:00
return ;
}
2019-02-04 13:20:32 -05:00
cerr < < " ... now doing the actual locate to " < < target_sample < < " from " < < _transport_sample < < endl ;
2017-08-08 19:26:08 -04:00
2011-03-05 18:16:32 -05:00
// Update Timecode time
2017-09-18 12:39:17 -04:00
_transport_sample = target_sample ;
2017-08-01 11:13:35 -04:00
// Bump seek counter so that any in-process locate in the butler
// thread(s?) can restart.
g_atomic_int_inc ( & _seek_counter ) ;
2017-09-18 12:39:17 -04:00
_last_roll_or_reversal_location = target_sample ;
2017-09-28 00:31:12 -04:00
_remaining_latency_preroll = worst_latency_preroll ( ) ;
timecode_time ( _transport_sample , transmitting_timecode_time ) ; // XXX here?
2011-03-05 18:16:32 -05:00
2011-05-30 17:37:58 -04:00
/* do "stopped" stuff if:
*
* we are rolling AND
2017-09-28 00:31:12 -04:00
* no autoplay in effect AND
* we ' re not going to keep rolling after the locate AND
* ! ( playing a loop with JACK sync )
*
2011-05-30 17:37:58 -04:00
*/
2011-06-01 12:50:12 -04:00
2012-06-22 10:27:51 -04:00
bool transport_was_stopped = ! transport_rolling ( ) ;
2015-10-05 10:17:49 -04:00
2015-09-22 13:21:44 -04:00
if ( ! transport_was_stopped & & ( ! auto_play_legal | | ! config . get_auto_play ( ) ) & & ! with_roll & & ! ( synced_to_engine ( ) & & play_loop ) & &
2015-05-12 12:28:05 -04:00
( ! Profile - > get_trx ( ) | | ! ( config . get_external_sync ( ) & & ! synced_to_engine ( ) ) ) ) {
2009-11-08 11:28:21 -05:00
realtime_stop ( false , true ) ; // XXX paul - check if the 2nd arg is really correct
2012-06-22 10:27:51 -04:00
transport_was_stopped = true ;
2011-05-30 17:37:58 -04:00
} else {
/* otherwise tell the world that we located */
realtime_locate ( ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
2015-09-24 13:40:55 -04:00
if ( force | | ! for_loop_enabled | | loop_changing ) {
2008-06-02 17:41:35 -04:00
2009-11-08 11:28:21 -05:00
PostTransportWork todo = PostTransportLocate ;
2008-08-04 18:37:24 -04:00
2012-06-22 10:27:51 -04:00
if ( with_roll & & transport_was_stopped ) {
2009-11-08 11:28:21 -05:00
todo = PostTransportWork ( todo | PostTransportRoll ) ;
2011-06-01 12:50:12 -04:00
}
2008-06-02 17:41:35 -04:00
2009-11-08 11:28:21 -05:00
add_post_transport_work ( todo ) ;
2015-02-17 11:21:41 -05:00
need_butler = true ;
2015-10-05 10:17:49 -04:00
2008-06-02 17:41:35 -04:00
} else {
/* this is functionally what clear_clicks() does but with a tentative lock */
2012-07-25 13:48:55 -04:00
Glib : : Threads : : RWLock : : WriterLock clickm ( click_lock , Glib : : Threads : : TRY_LOCK ) ;
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
if ( clickm . locked ( ) ) {
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
for ( Clicks : : iterator i = clicks . begin ( ) ; i ! = clicks . end ( ) ; + + i ) {
delete * i ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
clicks . clear ( ) ;
}
}
if ( with_roll ) {
/* switch from input if we're going to roll */
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( ! config . get_auto_input ( ) ) ;
2008-06-02 17:41:35 -04:00
}
} else {
/* otherwise we're going to stop, so do the opposite */
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( true ) ;
2008-06-02 17:41:35 -04:00
}
}
/* cancel looped playback if transport pos outside of loop range */
if ( play_loop ) {
2012-06-22 10:27:51 -04:00
2010-08-09 12:40:31 -04:00
Location * al = _locations - > auto_loop_location ( ) ;
2008-08-04 18:37:24 -04:00
2012-06-22 10:27:51 -04:00
if ( al ) {
2017-09-18 12:39:17 -04:00
if ( _transport_sample < al - > start ( ) | | _transport_sample > = al - > end ( ) ) {
2008-06-02 17:41:35 -04:00
2012-06-22 10:27:51 -04:00
// located outside the loop: cancel looping directly, this is called from event handling context
2015-02-16 19:30:21 -05:00
have_looped = false ;
2015-10-05 10:17:49 -04:00
2014-04-17 09:47:06 -04:00
if ( ! Config - > get_loop_is_mode ( ) ) {
set_play_loop ( false , _transport_speed ) ;
2015-02-13 15:27:53 -05:00
} else {
2015-02-16 19:30:21 -05:00
if ( Config - > get_seamless_loop ( ) ) {
/* this will make the non_realtime_locate() in the butler
which then causes seek ( ) in tracks actually do the right
thing .
*/
set_track_loop ( false ) ;
}
2014-04-17 09:47:06 -04:00
}
2015-09-24 13:40:55 -04:00
2017-09-18 12:39:17 -04:00
} else if ( _transport_sample = = al - > start ( ) ) {
2012-06-22 10:27:51 -04:00
// located to start of loop - this is looping, basically
2015-09-24 13:40:55 -04:00
if ( ! have_looped ) {
/* first time */
if ( _last_roll_location ! = al - > start ( ) ) {
/* didn't start at loop start - playback must have
* started before loop since we ' ve now hit the loop
* end .
*/
add_post_transport_work ( PostTransportLocate ) ;
need_butler = true ;
2015-02-16 19:30:21 -05:00
}
2012-06-22 10:27:51 -04:00
2015-09-24 13:40:55 -04:00
}
2012-06-22 10:27:51 -04:00
2015-09-24 13:40:55 -04:00
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
2015-10-05 10:17:49 -04:00
2015-09-24 13:40:55 -04:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
2015-10-05 10:17:49 -04:00
2016-04-08 16:49:47 -04:00
if ( tr & & tr - > rec_enable_control ( ) - > get_value ( ) ) {
2015-09-24 13:40:55 -04:00
// tell it we've looped, so it can deal with the record state
2017-09-18 12:39:17 -04:00
tr - > transport_looped ( _transport_sample ) ;
2008-06-02 17:41:35 -04:00
}
}
2012-06-22 10:27:51 -04:00
have_looped = true ;
TransportLooped ( ) ; // EMIT SIGNAL
2008-06-02 17:41:35 -04:00
}
}
}
2008-08-04 18:37:24 -04:00
2015-02-17 11:21:41 -05:00
if ( need_butler ) {
_butler - > schedule_transport_work ( ) ;
}
2015-02-16 19:30:21 -05:00
2008-06-02 17:41:35 -04:00
loop_changing = false ;
2009-10-26 10:38:58 -04:00
_send_timecode_update = true ;
2009-07-20 19:42:37 -04:00
2010-07-02 20:11:33 -04:00
if ( with_mmc ) {
2017-09-18 12:39:17 -04:00
send_mmc_locate ( _transport_sample ) ;
2010-06-28 13:25:40 -04:00
}
2017-09-18 12:39:17 -04:00
_last_roll_location = _last_roll_or_reversal_location = _transport_sample ;
if ( ! synced_to_engine ( ) | | _transport_sample = = _engine . transport_sample ( ) ) {
2017-06-09 15:31:15 -04:00
Located ( ) ; /* EMIT SIGNAL */
}
2008-06-02 17:41:35 -04:00
}
/** Set the transport speed.
2011-04-19 21:24:34 -04:00
* Called from the process thread .
* @ param speed New speed
2008-06-02 17:41:35 -04:00
*/
void
2017-09-18 12:39:17 -04:00
Session : : set_transport_speed ( double speed , samplepos_t destination_sample , bool abort , bool clear_state , bool as_default )
2008-06-02 17:41:35 -04:00
{
2015-10-04 14:51:05 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " @ %5 Set transport speed to %1, abort = %2 clear_state = %3, current = %4 as_default %6 \n " ,
2017-09-18 12:39:17 -04:00
speed , abort , clear_state , _transport_speed , _transport_sample , as_default ) ) ;
2009-11-30 11:12:13 -05:00
2017-10-29 13:32:17 -04:00
/* max speed is somewhat arbitrary but based on guestimates regarding disk i/o capability
and user needs . We really need CD - style " skip " playback for ffwd and rewind .
*/
if ( speed > 0 ) {
speed = min ( 8.0 , speed ) ;
} else if ( speed < 0 ) {
speed = max ( - 8.0 , speed ) ;
}
double new_engine_speed = 1.0 ;
if ( speed ! = 0 ) {
new_engine_speed = fabs ( speed ) ;
if ( speed < 0 ) speed = - 1 ;
if ( speed > 0 ) speed = 1 ;
}
if ( _transport_speed = = speed & & new_engine_speed = = _engine_speed ) {
2012-07-26 11:40:11 -04:00
if ( as_default & & speed = = 0.0 ) { // => reset default transport speed. hacky or what?
_default_transport_speed = 1.0 ;
}
2008-06-02 17:41:35 -04:00
return ;
}
2017-10-29 13:32:17 -04:00
#if 0 // TODO pref: allow vari-speed recording
2011-08-10 16:21:18 -04:00
if ( actively_recording ( ) & & speed ! = 1.0 & & speed ! = 0.0 ) {
/* no varispeed during recording */
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " No varispeed during recording cur_speed %1, sample %2 \n " ,
_transport_speed , _transport_sample ) ) ;
2011-08-09 17:17:55 -04:00
return ;
}
2017-10-29 13:32:17 -04:00
# endif
2011-08-09 17:17:55 -04:00
2009-06-09 20:03:47 -04:00
_target_transport_speed = fabs ( speed ) ;
2017-10-29 13:32:17 -04:00
_engine_speed = new_engine_speed ;
2008-06-02 17:41:35 -04:00
if ( transport_rolling ( ) & & speed = = 0.0 ) {
/* we are rolling and we want to stop */
2011-03-05 18:16:32 -05:00
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( true ) ;
2008-06-02 17:41:35 -04:00
}
2015-10-05 10:17:49 -04:00
2013-09-19 17:34:23 -04:00
if ( synced_to_engine ( ) ) {
2009-11-08 11:28:21 -05:00
if ( clear_state ) {
2011-06-01 12:50:12 -04:00
/* do this here because our response to the slave won't
2009-11-08 11:28:21 -05:00
take care of it .
*/
_play_range = false ;
2017-02-13 16:59:20 -05:00
_count_in_once = false ;
2009-11-08 11:28:21 -05:00
unset_play_loop ( ) ;
}
2008-06-02 17:41:35 -04:00
_engine . transport_stop ( ) ;
} else {
2015-01-16 12:17:09 -05:00
bool const auto_return_enabled = ( ! config . get_external_sync ( ) & & ( Config - > get_auto_return_target_list ( ) | | abort ) ) ;
2014-10-10 13:22:45 -04:00
if ( ! auto_return_enabled ) {
2017-09-18 12:39:17 -04:00
_requested_return_sample = destination_sample ;
2014-10-10 13:22:45 -04:00
}
2008-06-02 17:41:35 -04:00
stop_transport ( abort ) ;
}
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
} else if ( transport_stopped ( ) & & speed = = 1.0 ) {
2015-12-13 12:16:57 -05:00
if ( as_default ) {
_default_transport_speed = speed ;
}
2008-06-02 17:41:35 -04:00
/* we are stopped and we want to start rolling at speed 1 */
2014-04-17 09:47:06 -04:00
if ( Config - > get_loop_is_mode ( ) & & play_loop ) {
Location * location = _locations - > auto_loop_location ( ) ;
2015-10-05 10:17:49 -04:00
2014-04-17 09:47:06 -04:00
if ( location ! = 0 ) {
2017-09-18 12:39:17 -04:00
if ( _transport_sample ! = location - > start ( ) ) {
2015-02-16 19:30:21 -05:00
2015-02-13 18:42:41 -05:00
if ( Config - > get_seamless_loop ( ) ) {
/* force tracks to do their thing */
set_track_loop ( true ) ;
}
2015-02-16 19:30:21 -05:00
2014-04-17 09:47:06 -04:00
/* jump to start and then roll from there */
2015-02-16 19:30:21 -05:00
2014-04-17 09:47:06 -04:00
request_locate ( location - > start ( ) , true ) ;
return ;
}
}
}
2011-03-05 18:16:32 -05:00
if ( Config - > get_monitoring_model ( ) = = HardwareMonitoring & & config . get_auto_input ( ) ) {
2011-03-14 17:53:10 -04:00
set_track_monitor_input_status ( false ) ;
2008-06-02 17:41:35 -04:00
}
2013-09-19 17:34:23 -04:00
if ( synced_to_engine ( ) ) {
2008-06-02 17:41:35 -04:00
_engine . transport_start ( ) ;
2017-02-13 16:59:20 -05:00
_count_in_once = false ;
2008-06-02 17:41:35 -04:00
} else {
start_transport ( ) ;
}
} else {
2011-06-01 12:50:12 -04:00
2011-05-11 20:17:51 -04:00
/* not zero, not 1.0 ... varispeed */
2008-06-02 17:41:35 -04:00
2017-09-28 00:31:12 -04:00
// TODO handled transport start.. _remaining_latency_preroll
// and reversal of playback direction.
2013-09-19 17:34:23 -04:00
if ( ( synced_to_engine ( ) ) & & speed ! = 0.0 & & speed ! = 1.0 ) {
2011-03-14 17:53:10 -04:00
warning < < string_compose (
_ ( " Global varispeed cannot be supported while %1 is connected to JACK transport control " ) ,
PROGRAM_NAME )
2008-06-02 17:41:35 -04:00
< < endmsg ;
return ;
}
2017-10-29 13:32:17 -04:00
#if 0
2008-06-02 17:41:35 -04:00
if ( actively_recording ( ) ) {
return ;
}
2017-10-29 13:32:17 -04:00
# endif
2008-06-02 17:41:35 -04:00
2017-09-18 12:39:17 -04:00
if ( speed > 0.0 & & _transport_sample = = current_end_sample ( ) ) {
2008-06-02 17:41:35 -04:00
return ;
}
2017-09-18 12:39:17 -04:00
if ( speed < 0.0 & & _transport_sample = = 0 ) {
2008-06-02 17:41:35 -04:00
return ;
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
clear_clicks ( ) ;
/* if we are reversing relative to the current speed, or relative to the speed
before the last stop , then we have to do extra work .
*/
2011-06-01 12:50:12 -04:00
2009-11-08 11:28:21 -05:00
PostTransportWork todo = PostTransportWork ( 0 ) ;
2008-06-02 17:41:35 -04:00
2015-08-21 22:33:51 -04:00
if ( ( _transport_speed & & speed * _transport_speed < 0.0 ) | | ( _last_transport_speed * speed < 0.0 ) | | ( _last_transport_speed = = 0.0 & & speed < 0.0 ) ) {
2009-11-08 11:28:21 -05:00
todo = PostTransportWork ( todo | PostTransportReverse ) ;
2017-09-18 12:39:17 -04:00
_last_roll_or_reversal_location = _transport_sample ;
2008-06-02 17:41:35 -04:00
}
2008-08-04 18:37:24 -04:00
2008-06-02 17:41:35 -04:00
_last_transport_speed = _transport_speed ;
_transport_speed = speed ;
2008-08-04 18:37:24 -04:00
2012-06-22 10:45:44 -04:00
if ( as_default ) {
_default_transport_speed = speed ;
}
2009-11-08 11:28:21 -05:00
if ( todo ) {
add_post_transport_work ( todo ) ;
2009-10-23 20:39:28 -04:00
_butler - > schedule_transport_work ( ) ;
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
2012-07-16 23:10:40 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " send TSC3 with speed = %1 \n " , _transport_speed ) ) ;
2015-03-10 05:46:24 -04:00
2015-10-04 14:51:05 -04:00
/* throttle signal emissions.
2015-03-10 05:46:24 -04:00
* when slaved [ _last ] _transport_speed
* usually changes every cycle ( tiny amounts due to DLL ) .
* Emitting a signal every cycle is overkill and unwarranted .
*
* Using _last_transport_speed is not acceptable ,
2015-10-04 14:51:05 -04:00
* since it allows for large changes over a long period
2015-03-10 05:46:24 -04:00
* of time . Hence we introduce a dedicated variable to keep track
*
* The 0.2 % dead - zone is somewhat arbitrary . Main use - case
* for TransportStateChange ( ) here is the ShuttleControl display .
*/
2017-10-29 13:32:17 -04:00
if ( fabs ( _signalled_varispeed - actual_speed ( ) ) > .002
2015-03-10 05:46:24 -04:00
// still, signal hard changes to 1.0 and 0.0:
2017-10-29 13:32:17 -04:00
| | ( actual_speed ( ) = = 1.0 & & _signalled_varispeed ! = 1.0 )
| | ( actual_speed ( ) = = 0.0 & & _signalled_varispeed ! = 0.0 )
2015-03-10 05:46:24 -04:00
)
{
TransportStateChange ( ) ; /* EMIT SIGNAL */
2017-10-29 13:32:17 -04:00
_signalled_varispeed = actual_speed ( ) ;
2015-03-10 05:46:24 -04:00
}
2008-06-02 17:41:35 -04:00
}
}
/** Stop the transport. */
void
2009-11-08 11:28:21 -05:00
Session : : stop_transport ( bool abort , bool clear_state )
2008-06-02 17:41:35 -04:00
{
2017-02-13 16:59:20 -05:00
_count_in_once = false ;
2008-06-02 17:41:35 -04:00
if ( _transport_speed = = 0.0f ) {
return ;
}
2008-08-04 18:37:24 -04:00
2018-05-22 12:09:26 -04:00
DEBUG_TRACE ( DEBUG : : Transport , " time to actually stop \n " ) ;
2010-04-12 18:35:06 -04:00
2018-05-22 12:09:26 -04:00
realtime_stop ( abort , clear_state ) ;
_butler - > schedule_transport_work ( ) ;
2008-06-02 17:41:35 -04:00
}
2011-04-19 21:24:34 -04:00
/** Called from the process thread */
2008-06-02 17:41:35 -04:00
void
Session : : start_transport ( )
{
2011-05-11 20:17:51 -04:00
DEBUG_TRACE ( DEBUG : : Transport , " start_transport \n " ) ;
2017-09-18 12:39:17 -04:00
_last_roll_location = _transport_sample ;
_last_roll_or_reversal_location = _transport_sample ;
2017-09-28 00:31:12 -04:00
_remaining_latency_preroll = worst_latency_preroll ( ) ;
2011-06-01 12:50:12 -04:00
2008-12-12 09:43:24 -05:00
have_looped = false ;
2008-06-02 17:41:35 -04:00
/* if record status is Enabled, move it to Recording. if its
2008-08-04 18:37:24 -04:00
already Recording , move it to Disabled .
2008-06-02 17:41:35 -04:00
*/
switch ( record_status ( ) ) {
case Enabled :
2017-09-29 15:01:50 -04:00
if ( ! config . get_punch_in ( ) ) {
2017-09-29 20:45:13 -04:00
/* This is only for UIs (keep blinking rec-en before
* punch - in , don ' t show rec - region etc ) . The UI still
* depends on SessionEvent : : PunchIn and ensuing signals .
*
* The disk - writers handle punch in / out internally
* in their local delay - compensated timeframe .
*/
2008-06-02 17:41:35 -04:00
enable_record ( ) ;
}
break ;
case Recording :
if ( ! play_loop ) {
disable_record ( false ) ;
}
break ;
default :
break ;
}
2012-06-22 10:45:44 -04:00
_transport_speed = _default_transport_speed ;
2012-06-22 10:27:51 -04:00
_target_transport_speed = _transport_speed ;
2008-08-04 18:37:24 -04:00
2012-02-06 12:09:53 -05:00
if ( ! _engine . freewheeling ( ) ) {
Timecode : : Time time ;
2017-09-18 12:39:17 -04:00
timecode_time_subframes ( _transport_sample , time ) ;
2018-09-18 18:51:59 -04:00
if ( transport_master ( ) - > type ( ) = = MTC ) {
2014-10-22 17:06:53 -04:00
send_immediate_mmc ( MIDI : : MachineControlCommand ( MIDI : : MachineControl : : cmdDeferredPlay ) ) ;
2012-02-06 12:09:53 -05:00
}
2017-01-17 14:43:55 -05:00
2018-12-28 11:32:38 -05:00
if ( ( actively_recording ( ) | | ( config . get_punch_in ( ) & & get_record_enabled ( ) ) )
& & click_data & & ( config . get_count_in ( ) | | _count_in_once ) ) {
2017-02-13 16:59:20 -05:00
_count_in_once = false ;
2017-01-17 14:43:55 -05:00
/* calculate count-in duration (in audio samples)
2017-09-18 12:39:17 -04:00
* - use [ fixed ] tempo / meter at _transport_sample
* - calc duration of 1 bar + time - to - beat before or at transport_sample
2017-01-17 14:43:55 -05:00
*/
2017-09-18 12:39:17 -04:00
const Tempo & tempo = _tempo_map - > tempo_at_sample ( _transport_sample ) ;
const Meter & meter = _tempo_map - > meter_at_sample ( _transport_sample ) ;
2017-01-17 14:43:55 -05:00
2017-03-30 16:05:55 -04:00
const double num = meter . divisions_per_bar ( ) ;
const double den = meter . note_divisor ( ) ;
2017-09-18 12:39:17 -04:00
const double barbeat = _tempo_map - > exact_qn_at_sample ( _transport_sample , 0 ) * den / ( 4. * num ) ;
2017-03-30 16:05:55 -04:00
const double bar_fract = fmod ( barbeat , 1.0 ) ; // fraction of bar elapsed.
2017-01-17 14:43:55 -05:00
2017-09-18 12:39:17 -04:00
_count_in_samples = meter . samples_per_bar ( tempo , _current_sample_rate ) ;
2017-01-17 14:43:55 -05:00
2017-03-30 16:05:55 -04:00
double dt = _count_in_samples / num ;
if ( bar_fract = = 0 ) {
2017-01-17 14:43:55 -05:00
/* at bar boundary, count-in 2 bars before start. */
_count_in_samples * = 2 ;
} else {
/* beats left after full bar until roll position */
2017-03-30 16:05:55 -04:00
_count_in_samples * = 1. + bar_fract ;
2017-01-17 14:43:55 -05:00
}
2017-09-28 00:31:12 -04:00
if ( _count_in_samples > _remaining_latency_preroll ) {
_remaining_latency_preroll = _count_in_samples ;
}
2017-01-17 14:43:55 -05:00
int clickbeat = 0 ;
2017-09-18 12:39:17 -04:00
samplepos_t cf = _transport_sample - _count_in_samples ;
2017-09-28 00:31:12 -04:00
samplecnt_t offset = _click_io - > connected_latency ( true ) ;
while ( cf < _transport_sample + offset ) {
add_click ( cf , clickbeat = = 0 ) ;
2017-01-17 14:43:55 -05:00
cf + = dt ;
2017-03-30 16:05:55 -04:00
clickbeat = fmod ( clickbeat + 1 , num ) ;
2017-01-17 14:43:55 -05:00
}
2017-09-28 00:31:12 -04:00
if ( _count_in_samples < _remaining_latency_preroll ) {
_count_in_samples = _remaining_latency_preroll ;
}
2017-01-17 14:43:55 -05:00
}
2011-03-14 17:53:10 -04:00
}
2008-06-02 17:41:35 -04:00
2012-07-16 23:10:40 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " send TSC4 with speed = %1 \n " , _transport_speed ) ) ;
2008-06-02 17:41:35 -04:00
TransportStateChange ( ) ; /* EMIT SIGNAL */
}
/** Do any transport work in the audio thread that needs to be done after the
* transport thread is finished . Audio thread , realtime safe .
*/
void
Session : : post_transport ( )
{
2009-11-08 11:28:21 -05:00
PostTransportWork ptw = post_transport_work ( ) ;
if ( ptw & PostTransportAudition ) {
2010-03-24 23:40:07 -04:00
if ( auditioner & & auditioner - > auditioning ( ) ) {
2008-06-02 17:41:35 -04:00
process_function = & Session : : process_audition ;
} else {
process_function = & Session : : process_with_events ;
}
}
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportStop ) {
2008-06-02 17:41:35 -04:00
transport_sub_state = 0 ;
}
2009-11-08 11:28:21 -05:00
if ( ptw & PostTransportLocate ) {
2008-06-02 17:41:35 -04:00
2009-11-09 15:05:18 -05:00
if ( ( ( ! config . get_external_sync ( ) & & ( auto_play_legal & & config . get_auto_play ( ) ) ) & & ! _exporting ) | | ( ptw & PostTransportRoll ) ) {
2017-02-13 16:59:20 -05:00
_count_in_once = false ;
2008-06-02 17:41:35 -04:00
start_transport ( ) ;
} else {
transport_sub_state = 0 ;
}
}
set_next_event ( ) ;
2009-11-08 11:28:21 -05:00
/* XXX is this really safe? shouldn't we just be unsetting the bits that we actually
know were handled ?
*/
set_post_transport_work ( PostTransportWork ( 0 ) ) ;
2008-06-02 17:41:35 -04:00
}
void
2009-11-08 11:28:21 -05:00
Session : : unset_play_range ( )
2008-06-02 17:41:35 -04:00
{
2009-11-08 11:28:21 -05:00
_play_range = false ;
2009-12-03 21:15:12 -05:00
_clear_event_type ( SessionEvent : : RangeStop ) ;
_clear_event_type ( SessionEvent : : RangeLocate ) ;
2008-06-02 17:41:35 -04:00
}
void
2009-11-08 11:28:21 -05:00
Session : : set_play_range ( list < AudioRange > & range , bool leave_rolling )
2008-06-02 17:41:35 -04:00
{
2009-12-03 21:15:12 -05:00
SessionEvent * ev ;
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
/* Called from event-processing context */
2008-06-02 17:41:35 -04:00
2009-11-08 11:28:21 -05:00
unset_play_range ( ) ;
2011-06-01 12:50:12 -04:00
2009-11-08 11:28:21 -05:00
if ( range . empty ( ) ) {
/* _play_range set to false in unset_play_range()
*/
if ( ! leave_rolling ) {
/* stop transport */
2009-12-03 21:15:12 -05:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetTransportSpeed , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0.0f , false ) ;
2009-11-08 11:28:21 -05:00
merge_event ( ev ) ;
}
2008-06-02 17:41:35 -04:00
return ;
}
2009-11-08 11:28:21 -05:00
_play_range = true ;
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
/* cancel loop play */
unset_play_loop ( ) ;
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
list < AudioRange > : : size_type sz = range . size ( ) ;
2011-06-01 12:50:12 -04:00
2009-11-08 11:28:21 -05:00
if ( sz > 1 ) {
2011-06-01 12:50:12 -04:00
list < AudioRange > : : iterator i = range . begin ( ) ;
2008-06-02 17:41:35 -04:00
list < AudioRange > : : iterator next ;
2011-06-01 12:50:12 -04:00
2009-11-08 11:28:21 -05:00
while ( i ! = range . end ( ) ) {
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
next = i ;
+ + next ;
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
/* locating/stopping is subject to delays for declicking.
*/
2011-06-01 12:50:12 -04:00
2017-09-18 12:39:17 -04:00
samplepos_t requested_sample = i - > end ;
2011-06-01 12:50:12 -04:00
2017-09-18 12:39:17 -04:00
if ( requested_sample > current_block_size ) {
requested_sample - = current_block_size ;
2008-06-02 17:41:35 -04:00
} else {
2017-09-18 12:39:17 -04:00
requested_sample = 0 ;
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
2009-11-08 11:28:21 -05:00
if ( next = = range . end ( ) ) {
2017-09-18 12:39:17 -04:00
ev = new SessionEvent ( SessionEvent : : RangeStop , SessionEvent : : Add , requested_sample , 0 , 0.0f ) ;
2008-06-02 17:41:35 -04:00
} else {
2017-09-18 12:39:17 -04:00
ev = new SessionEvent ( SessionEvent : : RangeLocate , SessionEvent : : Add , requested_sample , ( * next ) . start , 0.0f ) ;
2008-06-02 17:41:35 -04:00
}
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
merge_event ( ev ) ;
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
i = next ;
}
2011-06-01 12:50:12 -04:00
2008-06-02 17:41:35 -04:00
} else if ( sz = = 1 ) {
2008-08-04 18:37:24 -04:00
2009-12-03 21:15:12 -05:00
ev = new SessionEvent ( SessionEvent : : RangeStop , SessionEvent : : Add , range . front ( ) . end , 0 , 0.0f ) ;
2008-06-02 17:41:35 -04:00
merge_event ( ev ) ;
2011-06-01 12:50:12 -04:00
}
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
/* save range so we can do auto-return etc. */
current_audio_range = range ;
2008-06-02 17:41:35 -04:00
/* now start rolling at the right place */
2008-08-04 18:37:24 -04:00
2009-12-03 21:15:12 -05:00
ev = new SessionEvent ( SessionEvent : : LocateRoll , SessionEvent : : Add , SessionEvent : : Immediate , range . front ( ) . start , 0.0f , false ) ;
2008-06-02 17:41:35 -04:00
merge_event ( ev ) ;
2011-06-01 12:50:12 -04:00
2012-07-16 23:10:40 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " send TSC5 with speed = %1 \n " , _transport_speed ) ) ;
2009-11-08 11:28:21 -05:00
TransportStateChange ( ) ;
2008-06-02 17:41:35 -04:00
}
void
2017-09-18 12:39:17 -04:00
Session : : request_bounded_roll ( samplepos_t start , samplepos_t end )
2008-06-02 17:41:35 -04:00
{
2009-11-08 11:28:21 -05:00
AudioRange ar ( start , end , 0 ) ;
list < AudioRange > lar ;
2008-06-02 17:41:35 -04:00
2009-11-08 11:28:21 -05:00
lar . push_back ( ar ) ;
request_play_range ( & lar , true ) ;
}
2014-07-03 13:25:35 -04:00
void
2017-09-18 12:39:17 -04:00
Session : : set_requested_return_sample ( samplepos_t return_to )
2014-07-03 13:25:35 -04:00
{
2017-09-18 12:39:17 -04:00
_requested_return_sample = return_to ;
2014-07-03 13:25:35 -04:00
}
2008-06-02 17:41:35 -04:00
void
2017-09-18 12:39:17 -04:00
Session : : request_roll_at_and_return ( samplepos_t start , samplepos_t return_to )
2008-06-02 17:41:35 -04:00
{
2009-12-03 21:15:12 -05:00
SessionEvent * ev = new SessionEvent ( SessionEvent : : LocateRollLocate , SessionEvent : : Add , SessionEvent : : Immediate , return_to , 1.0 ) ;
2017-09-18 12:39:17 -04:00
ev - > target2_sample = start ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
Session : : engine_halted ( )
{
bool ignored ;
/* there will be no more calls to process(), so
we ' d better clean up for ourselves , right now .
2008-08-04 18:37:24 -04:00
but first , make sure the butler is out of
2008-06-02 17:41:35 -04:00
the picture .
*/
2012-06-14 07:45:10 -04:00
if ( _butler ) {
_butler - > stop ( ) ;
}
2008-08-04 18:37:24 -04:00
2009-11-08 11:28:21 -05:00
realtime_stop ( false , true ) ;
2008-06-02 17:41:35 -04:00
non_realtime_stop ( false , 0 , ignored ) ;
transport_sub_state = 0 ;
2012-07-16 23:10:40 -04:00
DEBUG_TRACE ( DEBUG : : Transport , string_compose ( " send TSC6 with speed = %1 \n " , _transport_speed ) ) ;
2008-06-02 17:41:35 -04:00
TransportStateChange ( ) ; /* EMIT SIGNAL */
}
void
Session : : xrun_recovery ( )
{
2015-04-28 21:09:17 -04:00
+ + _xrun_count ;
2017-09-18 12:39:17 -04:00
Xrun ( _transport_sample ) ; /* EMIT SIGNAL */
2008-06-02 17:41:35 -04:00
if ( Config - > get_stop_recording_on_xrun ( ) & & actively_recording ( ) ) {
/* it didn't actually halt, but we need
to handle things in the same way .
*/
engine_halted ( ) ;
2008-08-04 18:37:24 -04:00
}
2008-06-02 17:41:35 -04:00
}
2009-11-30 18:16:28 -05:00
void
Session : : route_processors_changed ( RouteProcessorChange c )
{
2016-11-25 12:07:43 -05:00
if ( g_atomic_int_get ( & _ignore_route_processor_changes ) > 0 ) {
2012-01-17 20:30:44 -05:00
return ;
}
2009-11-30 18:16:28 -05:00
if ( c . type = = RouteProcessorChange : : MeterPointChange ) {
2015-04-26 23:01:07 -04:00
set_dirty ( ) ;
return ;
}
if ( c . type = = RouteProcessorChange : : RealTimeChange ) {
set_dirty ( ) ;
2009-11-30 18:16:28 -05:00
return ;
}
2011-03-14 16:33:47 -04:00
update_latency_compensation ( ) ;
2010-04-06 08:13:38 -04:00
resort_routes ( ) ;
2011-04-21 11:53:53 -04:00
set_dirty ( ) ;
2009-11-30 18:16:28 -05:00
}
2008-06-02 17:41:35 -04:00
void
Session : : allow_auto_play ( bool yn )
{
auto_play_legal = yn ;
}
2009-10-30 14:14:25 -04:00
bool
2017-09-18 12:39:17 -04:00
Session : : maybe_stop ( samplepos_t limit )
2009-10-30 14:14:25 -04:00
{
2017-09-18 12:39:17 -04:00
if ( ( _transport_speed > 0.0f & & _transport_sample > = limit ) | | ( _transport_speed < 0.0f & & _transport_sample = = 0 ) ) {
2013-09-19 17:34:23 -04:00
if ( synced_to_engine ( ) & & config . get_jack_time_master ( ) ) {
2011-03-14 17:53:10 -04:00
_engine . transport_stop ( ) ;
2013-09-19 17:34:23 -04:00
} else if ( ! synced_to_engine ( ) ) {
2011-03-14 17:53:10 -04:00
stop_transport ( ) ;
}
return true ;
}
return false ;
2009-10-30 14:14:25 -04:00
}
2010-07-04 21:13:36 -04:00
void
2017-09-18 12:39:17 -04:00
Session : : send_mmc_locate ( samplepos_t t )
2010-07-04 21:13:36 -04:00
{
2014-10-22 12:18:31 -04:00
if ( t < 0 ) {
return ;
}
2012-02-06 12:09:53 -05:00
if ( ! _engine . freewheeling ( ) ) {
Timecode : : Time time ;
timecode_time_subframes ( t , time ) ;
2014-10-22 17:06:53 -04:00
send_immediate_mmc ( MIDI : : MachineControlCommand ( time ) ) ;
2012-02-06 12:09:53 -05:00
}
2010-07-04 21:13:36 -04:00
}
2010-08-01 21:59:34 -04:00
/** Ask the transport to not send timecode until further notice. The suspension
* will come into effect some finite time after this call , and timecode_transmission_suspended ( )
* should be checked by the caller to find out when .
*/
void
Session : : request_suspend_timecode_transmission ( )
{
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetTimecodeTransmission , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0 , false ) ;
queue_event ( ev ) ;
}
void
Session : : request_resume_timecode_transmission ( )
{
SessionEvent * ev = new SessionEvent ( SessionEvent : : SetTimecodeTransmission , SessionEvent : : Add , SessionEvent : : Immediate , 0 , 0 , true ) ;
queue_event ( ev ) ;
}
bool
Session : : timecode_transmission_suspended ( ) const
{
return g_atomic_int_get ( & _suspend_timecode_transmission ) = = 1 ;
}
2018-09-18 18:51:59 -04:00
boost : : shared_ptr < TransportMaster >
Session : : transport_master ( ) const
{
return TransportMasterManager : : instance ( ) . current ( ) ;
}
bool
Session : : transport_master_is_external ( ) const
{
2019-01-25 00:05:20 -05:00
return TransportMasterManager : : instance ( ) . current ( ) & & config . get_external_sync ( ) ;
2018-09-18 18:51:59 -04:00
}
2019-02-28 19:18:08 -05:00
bool
Session : : transport_master_no_external_or_using_engine ( ) const
{
return ! TransportMasterManager : : instance ( ) . current ( ) | | ! config . get_external_sync ( ) | | ( TransportMasterManager : : instance ( ) . current ( ) - > type ( ) = = Engine ) ;
}
2018-09-18 18:51:59 -04:00
void
Session : : sync_source_changed ( SyncSource type , samplepos_t pos , pframes_t cycle_nframes )
{
/* Runs in process() context */
boost : : shared_ptr < TransportMaster > master = TransportMasterManager : : instance ( ) . current ( ) ;
/* save value of seamless from before the switch */
_was_seamless = Config - > get_seamless_loop ( ) ;
if ( type = = Engine ) {
/* JACK cannot support seamless looping at present */
Config - > set_seamless_loop ( false ) ;
} else {
/* reset to whatever the value was before we last switched slaves */
Config - > set_seamless_loop ( _was_seamless ) ;
}
if ( master - > can_loop ( ) ) {
request_play_loop ( false ) ;
} else if ( master - > has_loop ( ) ) {
request_play_loop ( true ) ;
}
/* slave change, reset any DiskIO block on disk output because it is no
longer valid with a new slave .
*/
DiskReader : : set_no_disk_output ( false ) ;
#if 0
we should not be treating specific transport masters as special cases because there maybe > 1 of a particular type
boost : : shared_ptr < MTC_TransportMaster > mtc_master = boost : : dynamic_pointer_cast < MTC_TransportMaster > ( master ) ;
if ( mtc_master ) {
mtc_master - > ActiveChanged . connect_same_thread ( mtc_status_connection , boost : : bind ( & Session : : mtc_status_changed , this , _1 ) ) ;
MTCSyncStateChanged ( mtc_master - > locked ( ) ) ;
} else {
if ( g_atomic_int_compare_and_exchange ( & _mtc_active , 1 , 0 ) ) {
MTCSyncStateChanged ( false ) ;
}
mtc_status_connection . disconnect ( ) ;
}
boost : : shared_ptr < LTC_TransportMaster > ltc_master = boost : : dynamic_pointer_cast < LTC_TransportMaster > ( master ) ;
if ( ltc_master ) {
ltc_master - > ActiveChanged . connect_same_thread ( ltc_status_connection , boost : : bind ( & Session : : ltc_status_changed , this , _1 ) ) ;
LTCSyncStateChanged ( ltc_master - > locked ( ) ) ;
} else {
if ( g_atomic_int_compare_and_exchange ( & _ltc_active , 1 , 0 ) ) {
LTCSyncStateChanged ( false ) ;
}
ltc_status_connection . disconnect ( ) ;
}
# endif
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " set new slave to %1 \n " , master ) ) ;
// need to queue this for next process() cycle
_send_timecode_update = true ;
boost : : shared_ptr < RouteList > rl = routes . reader ( ) ;
const bool externally_slaved = transport_master_is_external ( ) ;
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr & & ! tr - > is_private_route ( ) ) {
tr - > set_slaved ( externally_slaved ) ;
}
}
set_dirty ( ) ;
}