2008-06-02 17:41:35 -04:00
/*
2019-08-02 22:01:25 -04:00
* Copyright ( C ) 1999 - 2017 Paul Davis < paul @ linuxaudiosystems . com >
* Copyright ( C ) 2006 - 2012 David Robillard < d @ drobilla . net >
* Copyright ( C ) 2009 - 2012 Carl Hetherington < carl @ carlh . net >
* Copyright ( C ) 2015 - 2018 Robin Gareus < robin @ gareus . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
2008-06-02 17:41:35 -04:00
# include <cmath>
# include <unistd.h>
2009-02-25 13:26:51 -05:00
# include "pbd/error.h"
2009-11-08 11:28:21 -05:00
# include "pbd/enumwriter.h"
2013-07-11 14:58:00 -04:00
# include "pbd/pthread_utils.h"
2008-06-02 17:41:35 -04:00
2009-11-30 11:12:13 -05:00
# include "ardour/debug.h"
2009-12-03 21:15:12 -05:00
# include "ardour/session_event.h"
2022-07-06 22:44:05 -04:00
# include "ardour/track.h"
# include "ardour/region.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
2009-05-12 13:03:42 -04:00
using namespace std ;
2008-06-02 17:41:35 -04:00
using namespace ARDOUR ;
using namespace PBD ;
2009-12-04 14:24:09 -05:00
PerThreadPool * SessionEvent : : pool ;
void
SessionEvent : : init_event_pool ( )
{
pool = new PerThreadPool ;
}
2022-06-08 18:41:23 -04:00
guint
SessionEvent : : pool_available ( )
{
if ( ! pool | | ! pool - > per_thread_pool ( false ) ) {
return 0 ;
}
return pool - > per_thread_pool ( ) - > available ( ) ;
}
2015-02-24 07:27:36 -05:00
bool
SessionEvent : : has_per_thread_pool ( )
{
2015-05-13 17:32:10 -04:00
return pool - > has_per_thread_pool ( ) ;
2015-02-24 07:27:36 -05:00
}
2009-12-04 14:24:09 -05:00
void
2010-07-22 12:08:11 -04:00
SessionEvent : : create_per_thread_pool ( const std : : string & name , uint32_t nitems )
2009-12-04 14:24:09 -05:00
{
/* this is a per-thread call that simply creates a thread-private ptr to
a CrossThreadPool for use by this thread whenever events are allocated / released
from SessionEvent : : pool ( )
*/
2022-07-07 16:34:11 -04:00
pool - > create_per_thread_pool ( name , sizeof ( SessionEvent ) , nitems ,
# ifndef NDEBUG
[ ] ( size_t i , void * p ) { std : : cout < < i < < " " < < * static_cast < SessionEvent * > ( p ) < < " \n " ; }
# else
NULL
# endif
) ;
2009-12-04 14:24:09 -05:00
}
2017-09-18 12:39:17 -04:00
SessionEvent : : SessionEvent ( Type t , Action a , samplepos_t when , samplepos_t where , double spd , bool yn , bool yn2 , bool yn3 )
2015-02-05 16:20:09 -05:00
: type ( t )
, action ( a )
2017-09-18 12:39:17 -04:00
, action_sample ( when )
, target_sample ( where )
2015-02-05 16:20:09 -05:00
, speed ( spd )
, yes_or_no ( yn )
, second_yes_or_no ( yn2 )
, third_yes_or_no ( yn3 )
, event_loop ( 0 )
{
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " NEW SESSION EVENT, type = %1 action = %2 \n " , enum_2_string ( type ) , enum_2_string ( action ) ) ) ;
}
2009-12-04 14:24:09 -05:00
void *
2011-06-01 12:50:12 -04:00
SessionEvent : : operator new ( size_t )
2009-12-04 14:24:09 -05:00
{
CrossThreadPool * p = pool - > per_thread_pool ( ) ;
SessionEvent * ev = static_cast < SessionEvent * > ( p - > alloc ( ) ) ;
2015-02-05 16:20:09 -05:00
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " %1 Allocating SessionEvent from %2 ev @ %3 pool size %4 free %5 used %6 \n " , pthread_name ( ) , p - > name ( ) , ev ,
p - > total ( ) , p - > available ( ) , p - > used ( ) ) ) ;
2015-10-05 10:17:49 -04:00
2009-12-04 14:24:09 -05:00
ev - > own_pool = p ;
return ev ;
}
2011-06-01 12:50:12 -04:00
void
SessionEvent : : operator delete ( void * ptr , size_t /*size*/ )
2009-12-04 14:24:09 -05:00
{
2015-03-10 19:25:10 -04:00
Pool * p = pool - > per_thread_pool ( false ) ;
2009-12-04 14:24:09 -05:00
SessionEvent * ev = static_cast < SessionEvent * > ( ptr ) ;
2009-12-07 09:18:06 -05:00
2011-04-04 18:46:48 -04:00
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose (
2015-02-05 16:20:09 -05:00
" %1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9 \n " ,
pthread_name ( ) , ev , enum_2_string ( ev - > type ) , enum_2_string ( ev - > action ) , p - > name ( ) , ev - > own_pool - > name ( ) , ev - > own_pool - > total ( ) , ev - > own_pool - > available ( ) , ev - > own_pool - > used ( )
2011-04-04 18:46:48 -04:00
) ) ;
2011-01-08 10:19:32 -05:00
2015-03-10 19:25:10 -04:00
if ( p & & p = = ev - > own_pool ) {
2009-12-04 14:24:09 -05:00
p - > release ( ptr ) ;
} else {
2015-03-10 19:25:10 -04:00
assert ( ev - > own_pool ) ;
2009-12-04 14:24:09 -05:00
ev - > own_pool - > push ( ev ) ;
2015-02-05 16:20:09 -05:00
DEBUG_TRACE ( DEBUG : : SessionEvents , string_compose ( " %1 was wrong thread for this pool, pushed event onto pending list, will be deleted on next alloc from %2 pool size %3 free %4 used %5 pending %6 \n " ,
pthread_name ( ) , ev - > own_pool - > name ( ) ,
ev - > own_pool - > total ( ) , ev - > own_pool - > available ( ) , ev - > own_pool - > used ( ) ,
ev - > own_pool - > pending_size ( ) ) ) ;
2009-12-04 14:24:09 -05:00
}
}
2008-06-02 17:41:35 -04:00
void
2017-09-18 12:39:17 -04:00
SessionEventManager : : add_event ( samplepos_t sample , SessionEvent : : Type type , samplepos_t target_sample )
2008-06-02 17:41:35 -04:00
{
2017-09-18 12:39:17 -04:00
SessionEvent * ev = new SessionEvent ( type , SessionEvent : : Add , sample , target_sample , 0 ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
2017-09-18 12:39:17 -04:00
SessionEventManager : : remove_event ( samplepos_t sample , SessionEvent : : Type type )
2008-06-02 17:41:35 -04:00
{
2017-09-18 12:39:17 -04:00
SessionEvent * ev = new SessionEvent ( type , SessionEvent : : Remove , sample , 0 , 0 ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
2017-09-18 12:39:17 -04:00
SessionEventManager : : replace_event ( SessionEvent : : Type type , samplepos_t sample , samplepos_t target )
2008-06-02 17:41:35 -04:00
{
2021-05-12 13:13:06 -04:00
assert ( sample ! = SessionEvent : : Immediate ) ;
2017-09-18 12:39:17 -04:00
SessionEvent * ev = new SessionEvent ( type , SessionEvent : : Replace , sample , target , 0 ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
void
2009-12-03 21:15:12 -05:00
SessionEventManager : : clear_events ( SessionEvent : : Type type )
2008-06-02 17:41:35 -04:00
{
2014-11-24 02:52:14 -05:00
SessionEvent * ev = new SessionEvent ( type , SessionEvent : : Clear , SessionEvent : : Immediate , 0 , 0 ) ;
2008-06-02 17:41:35 -04:00
queue_event ( ev ) ;
}
2015-02-05 16:20:09 -05:00
void
SessionEventManager : : clear_events ( SessionEvent : : Type type , boost : : function < void ( void ) > after )
{
SessionEvent * ev = new SessionEvent ( type , SessionEvent : : Clear , SessionEvent : : Immediate , 0 , 0 ) ;
ev - > rt_slot = after ;
/* in the calling thread, after the clear is complete, arrange to flush things from the event
pool pending list ( i . e . to make sure they are really back in the free list and available
for future events ) .
*/
ev - > event_loop = PBD : : EventLoop : : get_event_loop_for_thread ( ) ;
if ( ev - > event_loop ) {
ev - > rt_return = boost : : bind ( & CrossThreadPool : : flush_pending_with_ev , ev - > event_pool ( ) , _1 ) ;
}
queue_event ( ev ) ;
}
2008-06-02 17:41:35 -04:00
void
2009-12-03 21:15:12 -05:00
SessionEventManager : : dump_events ( ) const
2008-06-02 17:41:35 -04:00
{
cerr < < " EVENT DUMP " < < endl ;
for ( Events : : const_iterator i = events . begin ( ) ; i ! = events . end ( ) ; + + i ) {
2014-10-10 13:20:28 -04:00
2022-01-05 12:26:10 -05:00
cerr < < " \t at " < < ( * i ) - > action_sample < < " type " < < enum_2_string ( ( * i ) - > type ) < < " target = " < < ( * i ) - > target_sample < < endl ;
2008-06-02 17:41:35 -04:00
}
cerr < < " Next event: " ;
2011-04-04 18:46:48 -04:00
if ( ( Events : : const_iterator ) next_event = = events . end ( ) ) {
2008-06-02 17:41:35 -04:00
cerr < < " none " < < endl ;
} else {
2017-09-18 12:39:17 -04:00
cerr < < " at " < < ( * next_event ) - > action_sample < < ' '
2014-10-10 13:20:28 -04:00
< < enum_2_string ( ( * next_event ) - > type ) < < " target = "
2017-09-18 12:39:17 -04:00
< < ( * next_event ) - > target_sample < < endl ;
2008-06-02 17:41:35 -04:00
}
cerr < < " Immediate events pending: \n " ;
for ( Events : : const_iterator i = immediate_events . begin ( ) ; i ! = immediate_events . end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
cerr < < " \t at " < < ( * i ) - > action_sample < < ' ' < < enum_2_string ( ( * i ) - > type ) < < " target = " < < ( * i ) - > target_sample < < endl ;
2008-06-02 17:41:35 -04:00
}
cerr < < " END EVENT_DUMP " < < endl ;
}
void
2009-12-03 21:15:12 -05:00
SessionEventManager : : merge_event ( SessionEvent * ev )
2008-06-02 17:41:35 -04:00
{
switch ( ev - > action ) {
2009-12-03 21:15:12 -05:00
case SessionEvent : : Remove :
2008-06-02 17:41:35 -04:00
_remove_event ( ev ) ;
delete ev ;
return ;
2009-12-03 21:15:12 -05:00
case SessionEvent : : Replace :
2008-06-02 17:41:35 -04:00
_replace_event ( ev ) ;
return ;
2009-12-03 21:15:12 -05:00
case SessionEvent : : Clear :
2008-06-02 17:41:35 -04:00
_clear_event_type ( ev - > type ) ;
2015-02-05 18:02:57 -05:00
/* run any additional realtime callback, if any */
if ( ev - > rt_slot ) {
ev - > rt_slot ( ) ;
}
2015-02-05 16:20:09 -05:00
if ( ev - > event_loop ) {
/* run non-realtime callback (in some other thread) */
ev - > event_loop - > call_slot ( MISSING_INVALIDATOR , boost : : bind ( ev - > rt_return , ev ) ) ;
} else {
delete ev ;
}
2008-06-02 17:41:35 -04:00
return ;
2009-10-14 12:10:01 -04:00
2009-12-03 21:15:12 -05:00
case SessionEvent : : Add :
2008-06-02 17:41:35 -04:00
break ;
}
/* try to handle immediate events right here */
2017-07-17 14:04:45 -04:00
if ( ev - > type = = SessionEvent : : Locate | | ev - > type = = SessionEvent : : LocateRoll ) {
/* remove any existing Locates that are waiting to execute */
_clear_event_type ( ev - > type ) ;
}
2017-09-18 12:39:17 -04:00
if ( ev - > action_sample = = SessionEvent : : Immediate ) {
2008-06-02 17:41:35 -04:00
process_event ( ev ) ;
return ;
}
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
switch ( ev - > type ) {
2009-12-03 21:15:12 -05:00
case SessionEvent : : AutoLoop :
2008-06-02 17:41:35 -04:00
_clear_event_type ( ev - > type ) ;
break ;
default :
for ( Events : : iterator i = events . begin ( ) ; i ! = events . end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
if ( ( * i ) - > type = = ev - > type & & ( * i ) - > action_sample = = ev - > action_sample ) {
error < < string_compose ( _ ( " Session: cannot have two events of type %1 at the same sample (%2). " ) ,
enum_2_string ( ev - > type ) , ev - > action_sample ) < < endmsg ;
2008-06-02 17:41:35 -04:00
return ;
}
}
}
events . insert ( events . begin ( ) , ev ) ;
2009-12-03 21:15:12 -05:00
events . sort ( SessionEvent : : compare ) ;
2008-06-02 17:41:35 -04:00
next_event = events . begin ( ) ;
set_next_event ( ) ;
}
/** @return true when @a ev is deleted. */
bool
2009-12-03 21:15:12 -05:00
SessionEventManager : : _replace_event ( SessionEvent * ev )
2008-06-02 17:41:35 -04:00
{
bool ret = false ;
Events : : iterator i ;
2021-03-23 23:05:35 -04:00
/* use only for events that can only exist once in the respective queue */
Events & e ( ev - > action_sample = = SessionEvent : : Immediate ? immediate_events : events ) ;
2008-06-02 17:41:35 -04:00
2021-03-23 23:05:35 -04:00
for ( i = e . begin ( ) ; i ! = e . end ( ) ; + + i ) {
2022-02-05 13:03:22 -05:00
if ( ( * i ) - > type = = ev - > type & & ev - > type = = SessionEvent : : Overwrite & & ( * i ) - > track . lock ( ) = = ev - > track . lock ( ) ) {
2021-03-23 23:05:35 -04:00
assert ( ev - > action_sample = = SessionEvent : : Immediate ) ;
2022-02-05 13:03:22 -05:00
( * i ) - > overwrite = ARDOUR : : OverwriteReason ( ( * i ) - > overwrite | ev - > overwrite ) ;
2021-03-21 21:54:42 -04:00
delete ev ;
2022-02-05 13:03:22 -05:00
return true ;
2021-03-21 21:54:42 -04:00
}
else if ( ( * i ) - > type = = ev - > type & & ev - > type ! = SessionEvent : : Overwrite ) {
2021-03-23 23:05:35 -04:00
assert ( ev - > action_sample ! = SessionEvent : : Immediate ) ;
2021-03-21 21:54:42 -04:00
assert ( ev - > type = = SessionEvent : : PunchIn | | ev - > type = = SessionEvent : : PunchOut | | ev - > type = = SessionEvent : : AutoLoop ) ;
2017-09-18 12:39:17 -04:00
( * i ) - > action_sample = ev - > action_sample ;
( * i ) - > target_sample = ev - > target_sample ;
2008-06-02 17:41:35 -04:00
if ( ( * i ) = = ev ) {
ret = true ;
}
delete ev ;
break ;
}
}
2021-03-23 23:05:35 -04:00
if ( i = = e . end ( ) ) {
e . insert ( e . begin ( ) , ev ) ;
2008-06-02 17:41:35 -04:00
}
2021-03-23 23:05:35 -04:00
if ( ev - > action_sample = = SessionEvent : : Immediate ) {
/* no need to sort immediate events */
return ret ;
}
e . sort ( SessionEvent : : compare ) ;
next_event = e . end ( ) ;
2008-06-02 17:41:35 -04:00
set_next_event ( ) ;
return ret ;
}
/** @return true when @a ev is deleted. */
bool
2009-12-03 21:15:12 -05:00
SessionEventManager : : _remove_event ( SessionEvent * ev )
2008-06-02 17:41:35 -04:00
{
bool ret = false ;
Events : : iterator i ;
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
for ( i = events . begin ( ) ; i ! = events . end ( ) ; + + i ) {
2017-09-18 12:39:17 -04:00
if ( ( * i ) - > type = = ev - > type & & ( * i ) - > action_sample = = ev - > action_sample ) {
2021-05-12 13:13:06 -04:00
assert ( ( * i ) - > action_sample ! = SessionEvent : : Immediate ) ;
2008-06-02 17:41:35 -04:00
if ( ( * i ) = = ev ) {
ret = true ;
}
delete * i ;
if ( i = = next_event ) {
+ + next_event ;
}
2014-03-18 15:23:28 -04:00
i = events . erase ( i ) ;
2008-06-02 17:41:35 -04:00
break ;
}
}
if ( i ! = events . end ( ) ) {
set_next_event ( ) ;
}
return ret ;
}
void
2009-12-03 21:15:12 -05:00
SessionEventManager : : _clear_event_type ( SessionEvent : : Type type )
2008-06-02 17:41:35 -04:00
{
Events : : iterator i , tmp ;
2009-10-14 12:10:01 -04:00
2008-06-02 17:41:35 -04:00
for ( i = events . begin ( ) ; i ! = events . end ( ) ; ) {
tmp = i ;
+ + tmp ;
if ( ( * i ) - > type = = type ) {
delete * i ;
if ( i = = next_event ) {
+ + next_event ;
}
events . erase ( i ) ;
}
i = tmp ;
}
for ( i = immediate_events . begin ( ) ; i ! = immediate_events . end ( ) ; ) {
tmp = i ;
+ + tmp ;
if ( ( * i ) - > type = = type ) {
delete * i ;
immediate_events . erase ( i ) ;
}
i = tmp ;
}
set_next_event ( ) ;
}
2022-07-06 22:44:05 -04:00
std : : ostream & operator < < ( std : : ostream & o , ARDOUR : : SessionEvent const & ev ) {
o < < " SessionEvent "
< < " type: " < < enum_2_string ( ev . type )
< < " action: " < < enum_2_string ( ev . action )
< < " atime: " < < ev . action_sample
< < " ttime: " < < ev . target_sample ;
switch ( ev . type ) {
case SessionEvent : : Locate :
o < < " disposition: " < < ev . locate_transport_disposition ;
o < < " force: " < < ev . yes_or_no ;
break ;
case SessionEvent : : LocateRoll :
o < < " force: " < < ev . yes_or_no ;
break ;
case SessionEvent : : SetDefaultPlaySpeed :
/* fallthrough */
case SessionEvent : : SetTransportSpeed :
o < < " speed: " < < ev . speed ;
break ;
case SessionEvent : : EndRoll :
o < < " abort: " < < ev . yes_or_no ;
o < < " clear: " < < ev . second_yes_or_no ;
break ;
case SessionEvent : : OverwriteAll :
o < < " reason: " < < ev . overwrite ;
break ;
case SessionEvent : : Audition :
o < < " region: ' " < < ev . region - > name ( ) < < " ' " ;
break ;
case SessionEvent : : Overwrite :
2023-02-16 18:33:28 -05:00
if ( std : : shared_ptr < Track > track = ev . track . lock ( ) ) {
2022-07-06 22:44:05 -04:00
o < < " track: ' " < < track - > name ( ) < < " ' " ;
}
o < < " reason: " < < ev . overwrite ;
break ;
default :
break ;
}
return o ;
}