2008-06-02 21:41:35 +00:00
/*
2009-10-14 16:10:01 +00:00
Copyright ( C ) 1999 - 2002 Paul Davis
2008-06-02 21:41:35 +00: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 .
*/
# include <cmath>
# include <cerrno>
# include <algorithm>
# include <unistd.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"
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"
# include "ardour/slave.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
2016-07-14 14:44:52 -04:00
# include "pbd/i18n.h"
2008-06-02 21:41:35 +00:00
using namespace ARDOUR ;
using namespace PBD ;
using namespace std ;
/** 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 ( ) ) {
2009-10-24 00:39:28 +00:00
if ( ! _butler - > transport_work_requested ( ) ) {
2008-06-02 21:41:35 +00:00
post_transport ( ) ;
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
*/
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
2015-04-27 04:52:14 +02:00
for ( RouteList : : const_iterator i = r - > begin ( ) ; i ! = r - > end ( ) ; + + i ) {
2015-04-28 22:18:30 +02:00
if ( ( * i ) - > apply_processor_changes_rt ( ) ) {
_rt_emit_pending = true ;
}
}
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
}
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
2017-09-18 12:39:17 -04:00
samplepos_t end_sample = _transport_sample + nframes ; // FIXME: varispeed + no_roll ??
2008-06-02 21:41:35 +00:00
int ret = 0 ;
2015-09-15 08:37:27 -04:00
int declick = ( config . get_use_transport_fades ( ) ? get_transport_declick_required ( ) : false ) ;
2008-06-02 21:41:35 +00:00
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-09-18 12:39:17 -04:00
ltc_tx_send_time_code_for_cycle ( _transport_sample , end_sample , _target_transport_speed , _transport_speed , nframes ) ;
2012-10-26 00:17:41 +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
}
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 " ) ;
2017-09-18 12:39:17 -04:00
_process_graph - > routes_no_roll ( nframes , _transport_sample , end_sample , non_realtime_work_pending ( ) , declick ) ;
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 ;
}
( * i ) - > set_pending_declick ( declick ) ;
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
{
2015-09-15 08:37:27 -04:00
int declick = ( config . get_use_transport_fades ( ) ? get_transport_declick_required ( ) : false ) ;
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
}
2017-07-05 17:24:02 -04: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/process-routes \n " ) ;
2017-09-18 12:39:17 -04:00
if ( _process_graph - > process_routes ( nframes , start_sample , end_sample , declick , 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 ;
}
( * i ) - > set_pending_declick ( declick ) ;
2012-01-30 20:58:17 +00:00
bool b = false ;
2017-09-18 12:39:17 -04:00
if ( ( ret = ( * i ) - > roll ( nframes , start_sample , end_sample , declick , b ) ) < 0 ) {
2011-04-04 22:46:48 +00:00
stop_transport ( ) ;
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 ( ) ;
}
}
/** 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
2017-09-28 06:31:12 +02:00
if ( _count_in_samples > 0 | | _remaining_latency_preroll > 0 ) {
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 ) ;
}
2017-09-30 13:10:17 +02:00
boost : : shared_ptr < RouteList > r = routes . reader ( ) ;
for ( RouteList : : 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 ) ) ;
}
}
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 ( ) ;
}
check_declick_out ( ) ;
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-07-28 12:10:04 -04:00
if ( _transport_speed = = 1.0 ) {
2017-09-18 12:39:17 -04:00
samples_moved = ( samplecnt_t ) nframes ;
2009-10-14 16:10:01 +00:00
} else {
2017-10-02 12:43:20 -04:00
/* use a cubic midi interpolation to compute the number of
* samples we will move at the current speed .
*/
CubicInterpolation interp ;
interp . set_speed ( _transport_speed ) ;
samples_moved = interp . distance ( nframes ) ;
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
2008-06-02 21:41:35 +00:00
if ( ! _exporting & & _slave ) {
2009-04-23 17:48:37 +00:00
if ( ! follow_slave ( 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
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 , end_sample , nframes ) ;
2008-06-02 21:41:35 +00:00
}
2017-09-18 12:39:17 -04:00
ltc_tx_send_time_code_for_cycle ( _transport_sample , end_sample , _target_transport_speed , _transport_speed , nframes ) ;
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 ) ) {
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 */
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 ) ;
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
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 ) ;
2008-06-02 21:41:35 +00:00
}
maybe_stop ( stop_limit ) ;
check_declick_out ( ) ;
}
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
}
void
Session : : reset_slave_state ( )
{
average_slave_delta = 1800 ;
delta_accumulator_cnt = 0 ;
have_first_delta_accumulator = false ;
2009-12-02 21:26:26 +00:00
_slave_state = Stopped ;
2017-07-27 17:27:49 -04:00
DiskReader : : set_no_disk_output ( false ) ;
2008-06-02 21:41:35 +00:00
}
bool
Session : : transport_locked ( ) const
{
Slave * sl = _slave ;
2009-11-09 20:05:18 +00:00
if ( ! locate_pending ( ) & & ( ! config . get_external_sync ( ) | | ( sl & & sl - > ok ( ) & & sl - > locked ( ) ) ) ) {
2008-06-02 21:41:35 +00:00
return true ;
}
return false ;
}
bool
2010-12-03 22:26:29 +00:00
Session : : follow_slave ( pframes_t nframes )
2008-06-02 21:41:35 +00:00
{
2009-01-09 09:18:24 +00:00
double slave_speed ;
2017-09-18 12:39:17 -04:00
samplepos_t slave_transport_sample ;
samplecnt_t this_delta ;
2008-06-02 21:41:35 +00:00
int dir ;
if ( ! _slave - > ok ( ) ) {
stop_transport ( ) ;
2009-11-09 20:05:18 +00:00
config . set_external_sync ( false ) ;
2008-06-02 21:41:35 +00:00
goto noroll ;
}
2009-10-14 16:10:01 +00:00
2017-09-18 12:39:17 -04:00
_slave - > speed_and_position ( slave_speed , slave_transport_sample ) ;
2008-06-02 21:41:35 +00:00
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " Slave position %1 speed %2 \n " , slave_transport_sample , slave_speed ) ) ;
2009-11-30 16:12:13 +00:00
2008-06-02 21:41:35 +00:00
if ( ! _slave - > locked ( ) ) {
2009-12-01 21:28:31 +00:00
DEBUG_TRACE ( DEBUG : : Slave , " slave not locked \n " ) ;
2008-06-02 21:41:35 +00:00
goto noroll ;
}
2017-09-18 12:39:17 -04:00
if ( slave_transport_sample > _transport_sample ) {
this_delta = slave_transport_sample - _transport_sample ;
2008-06-02 21:41:35 +00:00
dir = 1 ;
} else {
2017-09-18 12:39:17 -04:00
this_delta = _transport_sample - slave_transport_sample ;
2008-06-02 21:41:35 +00:00
dir = - 1 ;
}
2009-12-02 21:26:26 +00:00
if ( _slave - > starting ( ) ) {
2008-06-02 21:41:35 +00:00
slave_speed = 0.0f ;
}
2013-04-05 06:50:54 +02:00
if ( _slave - > is_always_synced ( ) | |
( Config - > get_timecode_source_is_synced ( ) & & ( dynamic_cast < TimecodeSlave * > ( _slave ) ) ! = 0 )
) {
2008-06-02 21:41:35 +00:00
2009-10-14 16:10:01 +00:00
/* if the TC source is synced, then we assume that its
2008-06-02 21:41:35 +00:00
speed is binary : 0.0 or 1.0
*/
if ( slave_speed ! = 0.0f ) {
slave_speed = 1.0f ;
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
} else {
2009-12-02 21:26:26 +00:00
/* if we are chasing and the average delta between us and the
master gets too big , we want to switch to silent
motion . so keep track of that here .
2008-06-02 21:41:35 +00:00
*/
2009-12-02 21:26:26 +00:00
if ( _slave_state = = Running ) {
2017-07-27 14:59:22 -04:00
calculate_moving_average_of_slave_delta ( dir , abs ( this_delta ) ) ;
2009-12-02 21:26:26 +00:00
}
2009-01-01 21:26:23 +00:00
}
2009-10-14 16:10:01 +00:00
2017-09-18 12:39:17 -04:00
track_slave_state ( slave_speed , slave_transport_sample , this_delta ) ;
2009-01-01 21:26:23 +00:00
2009-12-02 21:26:26 +00:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave state %1 @ %2 speed %3 cur delta %4 avg delta %5 \n " ,
2017-09-18 12:39:17 -04:00
_slave_state , slave_transport_sample , slave_speed , this_delta , average_slave_delta ) ) ;
2011-04-04 22:46:48 +00:00
2009-12-01 21:28:31 +00:00
2017-07-27 12:32:10 -04:00
if ( _slave_state = = Running & & ! _slave - > is_always_synced ( ) & & ! ( Config - > get_timecode_source_is_synced ( ) & & ( dynamic_cast < TimecodeSlave * > ( _slave ) ) ! = 0 ) ) {
/* may need to varispeed to sync with slave */
2009-01-01 21:26:23 +00:00
if ( _transport_speed ! = 0.0f ) {
2009-10-14 16:10:01 +00:00
/*
note that average_dir is + 1 or - 1
2009-01-01 21:26:23 +00:00
*/
2009-10-14 16:10:01 +00:00
2009-01-01 21:26:23 +00:00
float delta ;
2009-11-30 17:05:36 +00:00
if ( average_slave_delta = = 0 ) {
2009-01-01 21:26:23 +00:00
delta = this_delta ;
delta * = dir ;
2009-01-05 09:15:08 +00:00
} else {
2009-11-30 17:05:36 +00:00
delta = average_slave_delta ;
delta * = average_dir ;
2009-01-05 09:15:08 +00:00
}
2009-11-30 17:05:36 +00:00
2009-11-30 17:49:36 +00:00
# ifndef NDEBUG
2009-12-02 21:26:26 +00:00
if ( slave_speed ! = 0.0 ) {
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " delta = %1 speed = %2 ts = %3 M@%4 S@%5 avgdelta %6 \n " ,
( int ) ( dir * this_delta ) ,
slave_speed ,
_transport_speed ,
2017-09-18 12:39:17 -04:00
_transport_sample ,
slave_transport_sample ,
2009-12-02 21:26:26 +00:00
average_slave_delta ) ) ;
}
2009-11-30 17:49:36 +00:00
# endif
2011-04-04 22:46:48 +00:00
2009-12-02 21:26:26 +00:00
if ( _slave - > give_slave_full_control_over_transport_speed ( ) ) {
2014-10-10 13:22:45 -04:00
set_transport_speed ( slave_speed , 0 , false , false ) ;
2009-12-10 03:25:32 +00:00
//std::cout << "set speed = " << slave_speed << "\n";
2009-11-30 17:05:36 +00:00
} else {
2017-09-18 12:39:17 -04:00
float adjusted_speed = slave_speed + ( 1.5 * ( delta / float ( _current_sample_rate ) ) ) ;
2009-12-02 21:26:26 +00:00
request_transport_speed ( adjusted_speed ) ;
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " adjust using %1 towards %2 ratio %3 current %4 slave @ %5 \n " ,
delta , adjusted_speed , adjusted_speed / slave_speed , _transport_speed ,
slave_speed ) ) ;
2009-11-30 17:05:36 +00:00
}
2011-04-04 22:46:48 +00:00
2017-09-18 12:39:17 -04:00
if ( ! actively_recording ( ) & & ( samplecnt_t ) average_slave_delta > _slave - > resolution ( ) ) {
2017-07-27 17:39:45 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " average slave delta %1 greater than slave resolution %2 => no disk output \n " , average_slave_delta , _slave - > resolution ( ) ) ) ;
2017-07-27 12:32:10 -04:00
/* run routes as normal, but no disk output */
DiskReader : : set_no_disk_output ( true ) ;
return true ;
}
if ( ! have_first_delta_accumulator ) {
2017-07-27 17:39:45 -04:00
DEBUG_TRACE ( DEBUG : : Slave , " waiting for first slave delta accumulator to be ready, no disk output \n " ) ;
2017-07-27 12:32:10 -04:00
/* run routes as normal, but no disk output */
DiskReader : : set_no_disk_output ( true ) ;
return true ;
2009-01-01 21:26:23 +00:00
}
2008-06-02 21:41:35 +00:00
}
2009-10-14 16:10:01 +00:00
}
2009-01-01 21:26:23 +00:00
2017-07-27 12:32:10 -04:00
if ( ! have_first_delta_accumulator ) {
2017-07-27 17:39:45 -04:00
DEBUG_TRACE ( DEBUG : : Slave , " still waiting to compute slave delta, no disk output \n " ) ;
2017-07-27 12:32:10 -04:00
DiskReader : : set_no_disk_output ( true ) ;
} else {
DiskReader : : set_no_disk_output ( false ) ;
}
if ( ( _slave_state = = Running ) & & ( 0 = = ( post_transport_work ( ) & ~ PostTransportSpeed ) ) ) {
2009-01-01 21:26:23 +00:00
/* speed is set, we're locked, and good to go */
return true ;
}
2008-06-02 21:41:35 +00:00
2009-01-01 21:26:23 +00:00
noroll :
/* don't move at all */
2009-11-30 16:12:13 +00:00
DEBUG_TRACE ( DEBUG : : Slave , " no roll \n " )
2009-04-23 17:48:37 +00:00
no_roll ( nframes ) ;
2009-01-01 21:26:23 +00:00
return false ;
}
void
2017-09-18 12:39:17 -04:00
Session : : calculate_moving_average_of_slave_delta ( int dir , samplecnt_t this_delta )
2009-01-01 21:26:23 +00:00
{
if ( delta_accumulator_cnt > = delta_accumulator_size ) {
have_first_delta_accumulator = true ;
delta_accumulator_cnt = 0 ;
}
2017-09-18 12:39:17 -04:00
if ( delta_accumulator_cnt ! = 0 | | this_delta < _current_sample_rate ) {
delta_accumulator [ delta_accumulator_cnt + + ] = ( samplecnt_t ) dir * ( samplecnt_t ) this_delta ;
2009-01-01 21:26:23 +00:00
}
2009-10-14 16:10:01 +00:00
2009-01-01 21:26:23 +00:00
if ( have_first_delta_accumulator ) {
average_slave_delta = 0L ;
for ( int i = 0 ; i < delta_accumulator_size ; + + i ) {
average_slave_delta + = delta_accumulator [ i ] ;
}
2010-07-22 16:08:11 +00:00
average_slave_delta / = ( int32_t ) delta_accumulator_size ;
2009-01-01 21:26:23 +00:00
if ( average_slave_delta < 0L ) {
average_dir = - 1 ;
2017-07-27 14:59:22 -04:00
average_slave_delta = average_slave_delta ;
2009-01-01 21:26:23 +00:00
} else {
average_dir = 1 ;
}
2008-06-02 21:41:35 +00:00
}
2009-01-01 21:26:23 +00:00
}
2008-06-02 21:41:35 +00:00
2009-01-01 21:26:23 +00:00
void
2017-09-18 12:39:17 -04:00
Session : : track_slave_state ( float slave_speed , samplepos_t slave_transport_sample , samplecnt_t /*this_delta*/ )
2009-01-01 21:26:23 +00:00
{
2008-06-02 21:41:35 +00:00
if ( slave_speed ! = 0.0f ) {
/* slave is running */
2009-12-02 21:26:26 +00:00
switch ( _slave_state ) {
2008-06-02 21:41:35 +00:00
case Stopped :
if ( _slave - > requires_seekahead ( ) ) {
2017-09-18 12:39:17 -04:00
slave_wait_end = slave_transport_sample + _slave - > seekahead_distance ( ) ;
2009-12-01 21:42:45 +00:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave stopped, but running, requires seekahead to %1 \n " , slave_wait_end ) ) ;
2009-12-02 21:26:26 +00:00
/* we can call locate() here because we are in process context */
2008-06-02 21:41:35 +00:00
locate ( slave_wait_end , false , false ) ;
2009-12-02 21:26:26 +00:00
_slave_state = Waiting ;
2008-06-02 21:41:35 +00:00
} else {
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave stopped -> running at %1 \n " , slave_transport_sample ) ) ;
2012-10-16 20:53:12 +00:00
memset ( delta_accumulator , 0 , sizeof ( int32_t ) * delta_accumulator_size ) ;
average_slave_delta = 0L ;
2008-06-02 21:41:35 +00:00
2010-08-09 16:40:31 +00:00
Location * al = _locations - > auto_loop_location ( ) ;
2008-06-02 21:41:35 +00:00
2017-09-18 12:39:17 -04:00
if ( al & & play_loop & & ( slave_transport_sample < al - > start ( ) | | slave_transport_sample > al - > end ( ) ) ) {
2008-06-02 21:41:35 +00:00
// cancel looping
request_play_loop ( false ) ;
}
2017-09-18 12:39:17 -04:00
if ( slave_transport_sample ! = _transport_sample ) {
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " require locate to run. eng: %1 -> sl: %2 \n " , _transport_sample , slave_transport_sample ) ) ;
locate ( slave_transport_sample , false , false ) ;
2008-06-02 21:41:35 +00:00
}
2012-10-16 20:53:12 +00:00
_slave_state = Running ;
2008-06-02 21:41:35 +00:00
}
break ;
case Waiting :
2009-12-02 21:26:26 +00:00
default :
break ;
}
if ( _slave_state = = Waiting ) {
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave waiting at %1 \n " , slave_transport_sample ) ) ;
2008-06-02 21:41:35 +00:00
2017-09-18 12:39:17 -04:00
if ( slave_transport_sample > = slave_wait_end ) {
2008-06-02 21:41:35 +00:00
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave start at %1 vs %2 \n " , slave_transport_sample , _transport_sample ) ) ;
2008-06-02 21:41:35 +00:00
2009-12-02 21:26:26 +00:00
_slave_state = Running ;
/* now perform a "micro-seek" within the disk buffers to realign ourselves
precisely with the master .
*/
2008-06-02 21:41:35 +00:00
bool ok = true ;
2017-09-18 12:39:17 -04:00
samplecnt_t sample_delta = slave_transport_sample - _transport_sample ;
2008-06-02 21:41:35 +00:00
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 ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
2017-09-18 12:39:17 -04:00
if ( tr & & ! tr - > can_internal_playback_seek ( sample_delta ) ) {
2008-06-02 21:41:35 +00:00
ok = false ;
break ;
}
}
if ( ok ) {
2010-04-21 20:42:22 +00:00
for ( RouteList : : iterator i = rl - > begin ( ) ; i ! = rl - > end ( ) ; + + i ) {
boost : : shared_ptr < Track > tr = boost : : dynamic_pointer_cast < Track > ( * i ) ;
if ( tr ) {
2017-09-18 12:39:17 -04:00
tr - > internal_playback_seek ( sample_delta ) ;
2010-04-21 20:42:22 +00:00
}
2008-06-02 21:41:35 +00:00
}
2017-09-18 12:39:17 -04:00
_transport_sample + = sample_delta ;
2009-10-14 16:10:01 +00:00
2008-06-02 21:41:35 +00:00
} else {
2009-01-01 11:00:18 +00:00
cerr < < " cannot micro-seek \n " ;
2008-06-02 21:41:35 +00:00
/* XXX what? */
}
}
}
2009-10-14 16:10:01 +00:00
2009-12-02 21:26:26 +00:00
if ( _slave_state = = Running & & _transport_speed = = 0.0f ) {
2009-11-30 16:12:13 +00:00
DEBUG_TRACE ( DEBUG : : Slave , " slave starts transport \n " ) ;
2008-06-02 21:41:35 +00:00
start_transport ( ) ;
2009-10-14 16:10:01 +00:00
}
2008-06-02 21:41:35 +00:00
2009-01-01 21:26:23 +00:00
} else { // slave_speed is 0
2008-06-02 21:41:35 +00:00
/* slave has stopped */
if ( _transport_speed ! = 0.0f ) {
2017-09-18 12:39:17 -04:00
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave stops transport: %1 sample %2 tf %3 \n " , slave_speed , slave_transport_sample , _transport_sample ) ) ;
2011-02-07 17:01:43 +00:00
stop_transport ( ) ;
2008-06-02 21:41:35 +00:00
}
2017-09-18 12:39:17 -04:00
if ( slave_transport_sample ! = _transport_sample ) {
DEBUG_TRACE ( DEBUG : : Slave , string_compose ( " slave stopped, move to %1 \n " , slave_transport_sample ) ) ;
force_locate ( slave_transport_sample , false ) ;
2008-06-02 21:41:35 +00:00
}
2012-10-16 20:53:12 +00:00
reset_slave_state ( ) ;
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 ;
}
if ( ! _exporting & & _slave ) {
2009-04-23 17:48:37 +00:00
if ( ! follow_slave ( nframes ) ) {
2017-09-18 12:39:17 -04:00
ltc_tx_send_time_code_for_cycle ( _transport_sample , _transport_sample , 0 , 0 , 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 ) {
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
2017-07-28 12:10:04 -04:00
if ( _transport_speed = = 1.0 ) {
2017-09-18 12:39:17 -04:00
samples_moved = ( samplecnt_t ) nframes ;
2011-01-03 23:54:36 +00:00
} else {
2017-07-28 12:10:04 -04:00
interpolation . set_speed ( _transport_speed ) ;
2017-10-02 12:43:20 -04:00
samples_moved = interpolation . distance ( nframes ) ;
2011-01-03 23:54:36 +00:00
}
2011-04-04 22:46:48 +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
ltc_tx_send_time_code_for_cycle ( _transport_sample , _transport_sample + samples_moved , _target_transport_speed , _transport_speed , nframes ) ;
2012-10-21 14:07:10 +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 ) ;
} else if ( samples_moved ) {
increment_transport_position ( samples_moved ) ;
2008-06-02 21:41:35 +00:00
}
maybe_stop ( stop_limit ) ;
check_declick_out ( ) ;
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 ( ) ) {
2017-09-18 12:39:17 -04:00
_monitor_out - > monitor_run ( _transport_sample , _transport_sample + nframes , nframes , false ) ;
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 ( ) ) {
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 )
{
if ( _state_of_the_state & Deletion ) {
return ;
} else if ( _state_of_the_state & Loading ) {
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 :
2014-04-17 09:47:06 -04:00
set_play_loop ( ev - > yes_or_no , ev - > speed ) ;
2009-12-04 02:15:12 +00:00
break ;
case SessionEvent : : AutoLoop :
if ( play_loop ) {
2012-06-22 14:27:51 +00:00
/* roll after locate, do not flush, set "with loop"
true only if we are seamless looping
*/
2017-09-18 12:39:17 -04:00
start_locate ( ev - > target_sample , true , false , Config - > get_seamless_loop ( ) ) ;
2009-12-04 02:15:12 +00:00
}
remove = false ;
del = false ;
break ;
2012-06-20 18:46:05 +00:00
case SessionEvent : : AutoLoopDeclick :
if ( play_loop ) {
/* Request a declick fade-out and a fade-in; the fade-out will happen
at the end of the loop , and the fade - in at the start .
*/
transport_sub_state | = ( PendingLoopDeclickOut | PendingLoopDeclickIn ) ;
}
remove = false ;
del = false ;
break ;
2009-12-04 02:15:12 +00:00
case SessionEvent : : Locate :
2017-08-08 19:26:08 -04:00
if ( ev - > yes_or_no ) { /* force locate */
2012-06-22 14:27:51 +00:00
/* args: do not roll after locate, do flush, not with loop */
2017-09-18 12:39:17 -04:00
locate ( ev - > target_sample , false , true , false ) ;
2009-12-04 02:15:12 +00:00
} else {
2012-06-22 14:27:51 +00:00
/* args: do not roll after locate, do flush, not with loop */
2017-09-18 12:39:17 -04:00
start_locate ( ev - > target_sample , false , true , false ) ;
2009-12-04 02:15:12 +00:00
}
_send_timecode_update = true ;
break ;
case SessionEvent : : LocateRoll :
if ( ev - > yes_or_no ) {
2012-06-22 14:27:51 +00:00
/* args: roll after locate, do flush, not with loop */
2017-09-18 12:39:17 -04:00
locate ( ev - > target_sample , true , true , false ) ;
2009-12-04 02:15:12 +00:00
} else {
2012-06-22 14:27:51 +00:00
/* args: roll after locate, do flush, not with loop */
2017-09-18 12:39:17 -04:00
start_locate ( ev - > target_sample , true , true , false ) ;
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 ( ) ) {
2017-09-18 12:39:17 -04:00
start_locate ( ev - > target_sample , true , true , 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 ;
request_locate ( ev - > target2_sample , true ) ;
2009-12-04 02:15:12 +00:00
break ;
case SessionEvent : : SetTransportSpeed :
2017-09-18 12:39:17 -04:00
set_transport_speed ( ev - > speed , ev - > target_sample , ev - > yes_or_no , ev - > second_yes_or_no , ev - > third_yes_or_no ) ;
2009-12-04 02:15:12 +00:00
break ;
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 : : StopOnce :
if ( ! non_realtime_work_pending ( ) ) {
_clear_event_type ( SessionEvent : : StopOnce ) ;
2014-10-10 13:22:45 -04:00
stop_transport ( ev - > yes_or_no ) ;
2009-12-04 02:15:12 +00:00
}
remove = false ;
del = false ;
break ;
case SessionEvent : : RangeStop :
if ( ! non_realtime_work_pending ( ) ) {
stop_transport ( ev - > yes_or_no ) ;
}
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 */
2017-09-18 12:39:17 -04:00
start_locate ( ev - > target_sample , true , true , false ) ;
2009-12-04 02:15:12 +00:00
remove = false ;
del = false ;
break ;
case SessionEvent : : Overwrite :
2010-04-21 20:42:22 +00:00
overwrite_some_buffers ( static_cast < Track * > ( ev - > ptr ) ) ;
2009-12-04 02:15:12 +00:00
break ;
case SessionEvent : : SetSyncSource :
2012-10-12 22:04:21 +00:00
DEBUG_TRACE ( DEBUG : : Slave , " seen request for new slave \n " ) ;
2009-12-04 02:15:12 +00:00
use_sync_source ( ev - > slave ) ;
break ;
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
if ( _slave ) {
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 */
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 ) ;
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 ) ;
}