2021-11-04 16:00:16 -04:00
# include <algorithm>
2021-07-17 15:21:45 -04:00
# include <iostream>
2021-08-10 22:35:39 -04:00
# include <cstdlib>
2021-12-11 23:05:22 -05:00
# include <sstream>
2021-08-13 18:08:17 -04:00
2021-12-21 15:37:47 -05:00
# include <boost/make_shared.hpp>
2021-08-06 23:26:50 -04:00
# include <glibmm.h>
2021-08-06 15:03:28 -04:00
# include <rubberband/RubberBandStretcher.h>
2021-07-28 00:39:42 -04:00
# include "pbd/basename.h"
2021-08-08 21:08:16 -04:00
# include "pbd/compose.h"
2021-07-19 11:57:31 -04:00
# include "pbd/failed_constructor.h"
2021-11-09 23:47:15 -05:00
# include "pbd/pthread_utils.h"
2021-08-31 18:46:19 -04:00
# include "pbd/types_convert.h"
2021-07-19 11:57:31 -04:00
2021-08-05 17:03:28 -04:00
# include "temporal/tempo.h"
2021-11-11 17:10:33 -05:00
# include "ardour/audioengine.h"
2021-07-17 15:21:45 -04:00
# include "ardour/audioregion.h"
# include "ardour/audio_buffer.h"
2021-08-08 21:08:16 -04:00
# include "ardour/debug.h"
2021-12-15 20:18:18 -05:00
# include "ardour/import_status.h"
2021-07-17 15:21:45 -04:00
# include "ardour/midi_buffer.h"
2021-11-04 16:00:16 -04:00
# include "ardour/midi_model.h"
2021-10-20 18:19:09 -04:00
# include "ardour/midi_region.h"
2021-10-06 17:20:52 -04:00
# include "ardour/minibpm.h"
2021-10-08 22:35:25 -04:00
# include "ardour/port.h"
2021-07-17 15:21:45 -04:00
# include "ardour/region_factory.h"
2021-07-20 00:37:17 -04:00
# include "ardour/session.h"
2021-08-05 18:20:37 -04:00
# include "ardour/session_object.h"
2021-10-08 15:37:29 -04:00
# include "ardour/sidechain.h"
2021-09-01 00:35:14 -04:00
# include "ardour/source_factory.h"
2021-12-18 11:31:44 -05:00
# include "ardour/smf_source.h"
2021-07-17 15:21:45 -04:00
# include "ardour/sndfilesource.h"
# include "ardour/triggerbox.h"
2021-09-26 23:59:15 -04:00
# include "ardour/types_convert.h"
2021-07-17 15:21:45 -04:00
2021-09-27 09:49:41 -04:00
# include "pbd/i18n.h"
2021-07-17 15:21:45 -04:00
using namespace PBD ;
using namespace ARDOUR ;
using std : : string ;
using std : : cerr ;
using std : : endl ;
2021-08-07 18:20:36 -04:00
namespace ARDOUR {
namespace Properties {
2021-09-26 23:59:15 -04:00
PBD : : PropertyDescriptor < bool > use_follow ;
2021-08-07 18:20:36 -04:00
PBD : : PropertyDescriptor < bool > running ;
2021-11-15 14:27:32 -05:00
PBD : : PropertyDescriptor < bool > passthru ;
2021-09-05 12:40:58 -04:00
PBD : : PropertyDescriptor < bool > legato ;
2021-12-06 11:26:18 -05:00
PBD : : PropertyDescriptor < Temporal : : BBT_Offset > quantization ;
2021-10-01 20:39:06 -04:00
PBD : : PropertyDescriptor < Trigger : : LaunchStyle > launch_style ;
PBD : : PropertyDescriptor < Trigger : : FollowAction > follow_action0 ;
PBD : : PropertyDescriptor < Trigger : : FollowAction > follow_action1 ;
2021-12-21 15:37:47 -05:00
PBD : : PropertyDescriptor < uint32_t > currently_playing ;
2021-12-17 17:26:07 -05:00
PBD : : PropertyDescriptor < uint32_t > follow_count ;
2021-12-06 12:27:59 -05:00
PBD : : PropertyDescriptor < int > follow_action_probability ;
2021-12-06 10:47:29 -05:00
PBD : : PropertyDescriptor < float > velocity_effect ;
PBD : : PropertyDescriptor < gain_t > gain ;
2021-12-15 19:06:25 -05:00
PBD : : PropertyDescriptor < bool > stretchable ;
2021-12-17 13:39:38 -05:00
PBD : : PropertyDescriptor < bool > isolated ;
2021-08-07 18:20:36 -04:00
}
}
2021-12-22 00:35:59 -05:00
const Trigger * Trigger : : MagicClearPointerValue = ( Trigger * ) 0xfeedface ;
2021-12-21 15:37:47 -05:00
Trigger : : Trigger ( uint32_t n , TriggerBox & b )
2021-08-08 21:08:16 -04:00
: _box ( b )
, _state ( Stopped )
, _bang ( 0 )
, _unbang ( 0 )
, _index ( n )
2021-12-18 16:11:54 -05:00
, _pending_gain ( 1.0 )
, _loop_cnt ( 0 )
, _ui ( 0 )
, _explicitly_stopped ( false )
, _launch_style ( Properties : : launch_style , Toggle )
2021-09-26 23:59:15 -04:00
, _use_follow ( Properties : : use_follow , true )
2021-12-18 16:11:54 -05:00
, _follow_action0 ( Properties : : follow_action0 , NextTrigger )
, _follow_action1 ( Properties : : follow_action1 , Stop )
2021-12-14 14:09:43 -05:00
, _follow_action_probability ( Properties : : follow_action_probability , 100 )
2021-12-14 10:32:06 -05:00
, _follow_count ( Properties : : follow_count , 1 )
2021-12-18 16:11:54 -05:00
, _quantization ( Properties : : quantization , Temporal : : BBT_Offset ( 0 , 1 , 0 ) )
2021-10-14 15:11:08 -04:00
, _legato ( Properties : : legato , false )
2021-12-18 16:11:54 -05:00
, _name ( Properties : : name , " " )
2021-12-17 13:40:05 -05:00
, _gain ( Properties : : gain , 1.0 )
2021-12-14 14:09:43 -05:00
, _midi_velocity_effect ( Properties : : velocity_effect , 0. )
2021-12-15 19:06:25 -05:00
, _stretchable ( Properties : : stretchable , true )
2021-12-17 13:39:38 -05:00
, _isolated ( Properties : : isolated , false )
2021-12-18 16:11:54 -05:00
, _barcnt ( 0. )
, _apparent_tempo ( 0. )
, expected_end_sample ( 0 )
2021-12-21 19:36:39 -05:00
, _pending ( ( Trigger * ) 0 )
2021-08-07 18:20:36 -04:00
{
2021-12-18 16:11:54 -05:00
add_property ( _launch_style ) ;
2021-09-26 23:59:15 -04:00
add_property ( _use_follow ) ;
2021-12-18 16:11:54 -05:00
add_property ( _follow_action0 ) ;
add_property ( _follow_action1 ) ;
2021-12-14 14:09:43 -05:00
add_property ( _follow_action_probability ) ;
2021-12-18 16:11:54 -05:00
add_property ( _follow_count ) ;
add_property ( _quantization ) ;
add_property ( _legato ) ;
add_property ( _name ) ;
2021-12-17 13:40:05 -05:00
add_property ( _gain ) ;
2021-12-18 16:11:54 -05:00
add_property ( _midi_velocity_effect ) ;
2021-12-15 19:06:25 -05:00
add_property ( _stretchable ) ;
2021-12-17 13:39:38 -05:00
add_property ( _isolated ) ;
2021-09-26 23:59:15 -04:00
}
2021-12-21 19:36:39 -05:00
void
Trigger : : set_pending ( Trigger * t )
{
Trigger * old = _pending . exchange ( t ) ;
2021-12-22 00:35:59 -05:00
if ( old & & old ! = MagicClearPointerValue ) {
2021-12-21 19:36:39 -05:00
/* new pending trigger set before existing pending trigger was used */
delete old ;
}
}
Trigger *
Trigger : : swap_pending ( Trigger * t )
{
return _pending . exchange ( t ) ;
}
2021-09-26 23:59:15 -04:00
void
Trigger : : set_use_follow ( bool yn )
{
_use_follow = yn ;
2021-09-28 19:45:35 -04:00
PropertyChanged ( Properties : : use_follow ) ;
2021-08-07 18:20:36 -04:00
}
2021-08-31 13:53:24 -04:00
void
Trigger : : set_name ( std : : string const & str )
{
_name = str ;
}
2021-12-17 13:39:38 -05:00
void
Trigger : : set_scene_isolated ( bool i )
{
_isolated = i ;
PropertyChanged ( ARDOUR : : Properties : : isolated ) ;
}
void
Trigger : : set_stretchable ( bool s )
{
_stretchable = s ;
PropertyChanged ( ARDOUR : : Properties : : stretchable ) ;
}
2021-09-10 15:04:49 -04:00
void
Trigger : : set_ui ( void * p )
{
_ui = p ;
}
2021-08-08 21:08:16 -04:00
void
Trigger : : bang ( )
2021-07-17 15:21:45 -04:00
{
2021-10-08 15:37:29 -04:00
if ( ! _region ) {
return ;
}
2021-08-08 21:08:16 -04:00
_bang . fetch_add ( 1 ) ;
2021-08-30 18:31:35 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " bang on %1 \n " , _index ) ) ;
2021-07-20 00:37:17 -04:00
}
2021-08-08 21:08:16 -04:00
void
Trigger : : unbang ( )
2021-08-05 14:10:40 -04:00
{
2021-10-08 15:37:29 -04:00
if ( ! _region ) {
return ;
}
2021-08-08 21:08:16 -04:00
_unbang . fetch_add ( 1 ) ;
2021-08-30 18:31:35 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " un-bang on %1 \n " , _index ) ) ;
2021-08-05 14:10:40 -04:00
}
2021-10-19 14:52:32 -04:00
void
Trigger : : set_gain ( gain_t g )
{
_pending_gain = g ;
2021-12-06 10:47:29 -05:00
PropertyChanged ( Properties : : gain ) ;
2021-10-19 14:52:32 -04:00
}
void
Trigger : : set_midi_velocity_effect ( float mve )
{
_midi_velocity_effect = std : : min ( 1.f , std : : max ( 0.f , mve ) ) ;
2021-12-06 10:47:29 -05:00
PropertyChanged ( Properties : : velocity_effect ) ;
2021-10-19 14:52:32 -04:00
}
2021-10-07 00:40:37 -04:00
void
Trigger : : set_follow_count ( uint32_t n )
{
_follow_count = n ;
2021-12-06 10:47:29 -05:00
PropertyChanged ( Properties : : follow_count ) ;
2021-10-07 00:40:37 -04:00
}
2021-08-08 21:08:16 -04:00
void
2021-12-18 16:11:54 -05:00
Trigger : : set_follow_action ( FollowAction f , uint32_t n )
2021-07-17 15:21:45 -04:00
{
2021-08-12 00:54:13 -04:00
assert ( n < 2 ) ;
2021-12-18 16:11:54 -05:00
2021-10-01 20:39:06 -04:00
if ( n = = 0 ) {
2021-12-18 16:11:54 -05:00
_follow_action0 = f ;
2021-10-01 20:39:06 -04:00
PropertyChanged ( Properties : : follow_action0 ) ;
} else {
2021-12-18 16:11:54 -05:00
_follow_action1 = f ;
2021-10-01 20:39:06 -04:00
PropertyChanged ( Properties : : follow_action1 ) ;
}
2021-07-19 11:57:31 -04:00
}
2021-08-06 23:26:50 -04:00
void
2021-08-08 21:08:16 -04:00
Trigger : : set_launch_style ( LaunchStyle l )
2021-08-06 23:26:50 -04:00
{
2021-08-08 21:08:16 -04:00
_launch_style = l ;
2021-08-11 20:25:46 -04:00
set_usable_length ( ) ;
2021-10-01 20:39:06 -04:00
PropertyChanged ( Properties : : launch_style ) ;
2021-08-06 23:26:50 -04:00
}
2021-08-08 21:08:16 -04:00
XMLNode &
Trigger : : get_state ( void )
2021-07-19 11:57:31 -04:00
{
2021-08-08 21:08:16 -04:00
XMLNode * node = new XMLNode ( X_ ( " Trigger " ) ) ;
2021-08-31 18:46:19 -04:00
2021-09-26 23:59:15 -04:00
for ( OwnedPropertyList : : iterator i = _properties - > begin ( ) ; i ! = _properties - > end ( ) ; + + i ) {
i - > second - > get_value ( * node ) ;
}
2021-08-31 18:46:19 -04:00
node - > set_property ( X_ ( " index " ) , _index ) ;
2021-11-11 17:10:33 -05:00
node - > set_property ( X_ ( " apparent-tempo " ) , _apparent_tempo ) ;
node - > set_property ( X_ ( " barcnt " ) , _barcnt ) ;
2021-08-31 18:46:19 -04:00
if ( _region ) {
node - > set_property ( X_ ( " region " ) , _region - > id ( ) ) ;
}
2021-08-08 21:08:16 -04:00
return * node ;
2021-07-19 11:57:31 -04:00
}
2021-08-08 21:08:16 -04:00
int
2021-08-31 20:36:16 -04:00
Trigger : : set_state ( const XMLNode & node , int version )
2021-07-19 11:57:31 -04:00
{
2021-09-26 23:59:15 -04:00
PropertyChange what_changed ;
what_changed = set_values ( node ) ;
2021-08-31 20:36:16 -04:00
node . get_property ( X_ ( " index " ) , _index ) ;
2021-09-01 00:35:14 -04:00
PBD : : ID rid ;
node . get_property ( X_ ( " region " ) , rid ) ;
boost : : shared_ptr < Region > r = RegionFactory : : region_by_id ( rid ) ;
if ( r ) {
set_region ( r ) ;
}
2021-08-08 21:08:16 -04:00
return 0 ;
2021-07-17 15:21:45 -04:00
}
2021-08-12 01:09:36 -04:00
2021-09-05 12:40:58 -04:00
void
Trigger : : set_legato ( bool yn )
{
_legato = yn ;
PropertyChanged ( Properties : : legato ) ;
}
2021-08-12 01:09:36 -04:00
void
Trigger : : set_follow_action_probability ( int n )
{
n = std : : min ( 100 , n ) ;
n = std : : max ( 0 , n ) ;
_follow_action_probability = n ;
2021-12-06 11:26:18 -05:00
PropertyChanged ( Properties : : follow_action_probability ) ;
2021-08-12 01:09:36 -04:00
}
2021-08-08 21:08:16 -04:00
void
Trigger : : set_quantization ( Temporal : : BBT_Offset const & q )
2021-07-17 15:21:45 -04:00
{
2021-08-08 21:08:16 -04:00
_quantization = q ;
2021-08-11 20:25:46 -04:00
set_usable_length ( ) ;
2021-09-29 20:00:55 -04:00
PropertyChanged ( Properties : : quantization ) ;
2021-08-08 21:08:16 -04:00
}
2021-07-17 15:21:45 -04:00
2021-11-10 10:45:38 -05:00
void
Trigger : : set_region ( boost : : shared_ptr < Region > r )
{
2021-12-22 00:35:59 -05:00
/* Called from (G)UI thread */
2021-12-21 17:15:06 -05:00
if ( ! r ) {
/* clear operation, no need to talk to the worker thread */
2021-12-22 00:35:59 -05:00
set_pending ( ( Trigger * ) Trigger : : MagicClearPointerValue ) ;
2021-12-21 20:13:50 -05:00
request_stop ( ) ;
2021-12-21 17:15:06 -05:00
} else {
/* load data, do analysis in another thread */
TriggerBox : : worker - > set_region ( _box , index ( ) , r ) ;
}
2021-11-10 10:45:38 -05:00
}
2021-12-22 00:35:59 -05:00
void
Trigger : : clear_region ( )
{
/* Called from RT process thread */
_region . reset ( ) ;
}
2021-08-08 21:08:16 -04:00
void
Trigger : : set_region_internal ( boost : : shared_ptr < Region > r )
{
_region = r ;
2021-07-17 15:21:45 -04:00
}
2021-08-08 21:08:16 -04:00
Temporal : : BBT_Offset
Trigger : : quantization ( ) const
2021-07-17 15:21:45 -04:00
{
2021-08-08 21:08:16 -04:00
return _quantization ;
2021-07-17 15:21:45 -04:00
}
void
2021-11-29 23:50:19 -05:00
Trigger : : request_stop ( )
2021-07-17 15:21:45 -04:00
{
2021-11-29 23:50:19 -05:00
_requests . stop = true ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 asked to stop \n " , name ( ) ) ) ;
2021-08-05 17:03:28 -04:00
}
2021-08-08 21:08:16 -04:00
void
2021-09-04 12:37:36 -04:00
Trigger : : startup ( )
2021-07-17 15:21:45 -04:00
{
2021-09-04 12:37:36 -04:00
_state = WaitingToStart ;
2021-10-19 14:52:32 -04:00
_gain = _pending_gain ;
2021-10-26 19:34:06 -04:00
_loop_cnt = 0 ;
2021-12-15 13:03:47 -05:00
_explicitly_stopped = false ;
2021-10-26 19:34:06 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 starts up \n " , name ( ) ) ) ;
2021-09-04 12:37:36 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
}
2021-10-19 14:52:32 -04:00
void
Trigger : : shutdown ( )
{
_state = Stopped ;
_gain = 1.0 ;
2021-10-26 19:34:06 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 shuts down \n " , name ( ) ) ) ;
2021-10-19 14:52:32 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
}
2021-09-05 12:40:58 -04:00
void
Trigger : : jump_start ( )
{
/* this is used when we start a new trigger in legato mode. We do not
wait for quantization .
*/
_state = Running ;
2021-11-17 23:14:56 -05:00
/* XXX set expected_end_sample */
2021-10-14 23:08:19 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 requested state %2 \n " , index ( ) , enum_2_string ( _state ) ) ) ;
2021-09-05 12:40:58 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
}
void
Trigger : : jump_stop ( )
{
/* this is used when we start a new trigger in legato mode. We do not
wait for quantization .
*/
2021-10-19 14:52:32 -04:00
shutdown ( ) ;
2021-10-14 23:08:19 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 requested state %2 \n " , index ( ) , enum_2_string ( _state ) ) ) ;
PropertyChanged ( ARDOUR : : Properties : : running ) ;
}
void
2021-12-15 13:03:47 -05:00
Trigger : : begin_stop ( bool explicit_stop )
2021-10-14 23:08:19 -04:00
{
/* this is used when we start a tell a currently playing trigger to
stop , but wait for quantization first .
*/
_state = WaitingToStop ;
2021-12-15 13:03:47 -05:00
_explicitly_stopped = explicit_stop ;
2021-10-14 23:08:19 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 requested state %2 \n " , index ( ) , enum_2_string ( _state ) ) ) ;
2021-09-05 12:40:58 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
}
2021-07-19 11:57:31 -04:00
void
2021-08-08 21:08:16 -04:00
Trigger : : process_state_requests ( )
2021-07-19 11:57:31 -04:00
{
2021-11-29 23:50:19 -05:00
bool stop = _requests . stop . exchange ( false ) ;
2021-07-19 11:57:31 -04:00
2021-11-29 23:50:19 -05:00
if ( stop ) {
2021-12-04 13:48:42 -05:00
/* This is for an immediate stop, not a quantized one */
if ( _state ! = Stopped ) {
2021-11-29 23:50:19 -05:00
shutdown ( ) ;
2021-12-04 13:48:42 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 immediate stop implemented \n " , name ( ) ) ) ;
2021-08-10 22:35:39 -04:00
}
2021-12-04 13:48:42 -05:00
/* Don't process bang/unbang requests since we're stopping */
_bang = 0 ;
_unbang = 0 ;
return ;
2021-07-19 11:57:31 -04:00
}
2021-08-08 21:08:16 -04:00
/* now check bangs/unbangs */
2021-07-27 22:39:40 -04:00
2021-08-08 21:08:16 -04:00
int x ;
2021-07-27 22:39:40 -04:00
2021-08-08 21:08:16 -04:00
while ( ( x = _bang . load ( ) ) ) {
2021-08-05 17:03:28 -04:00
2021-08-08 21:08:16 -04:00
_bang . fetch_sub ( 1 ) ;
2021-08-05 17:03:28 -04:00
2021-08-10 22:35:39 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 handling bang with state = %2 \n " , index ( ) , enum_2_string ( _state ) ) ) ;
2021-08-08 21:08:16 -04:00
switch ( _state ) {
case Running :
switch ( launch_style ( ) ) {
2021-08-12 00:16:16 -04:00
case OneShot :
2021-09-05 12:40:58 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 oneshot %2 => %3 \n " , index ( ) , enum_2_string ( Running ) , enum_2_string ( WaitingForRetrigger ) ) ) ;
2021-08-10 22:35:39 -04:00
_state = WaitingForRetrigger ;
2021-08-31 13:53:24 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
2021-08-08 21:08:16 -04:00
break ;
case Gate :
case Toggle :
case Repeat :
2021-09-05 12:40:58 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 %2 gate/toggle/repeat => %3 \n " , index ( ) , enum_2_string ( Running ) , enum_2_string ( WaitingToStop ) ) ) ;
2021-12-15 13:03:47 -05:00
begin_stop ( true ) ;
2021-08-08 21:08:16 -04:00
}
break ;
2021-08-05 17:03:28 -04:00
2021-08-08 21:08:16 -04:00
case Stopped :
2021-09-05 12:40:58 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 %2 stopped => %3 \n " , index ( ) , enum_2_string ( Stopped ) , enum_2_string ( WaitingToStart ) ) ) ;
2021-12-21 15:37:47 -05:00
_box . queue_explict ( index ( ) ) ;
2021-08-08 21:08:16 -04:00
break ;
2021-08-05 17:03:28 -04:00
2021-08-08 21:08:16 -04:00
case WaitingToStart :
case WaitingToStop :
2021-08-10 22:35:39 -04:00
case WaitingForRetrigger :
2021-08-08 21:08:16 -04:00
case Stopping :
break ;
}
}
2021-07-17 15:21:45 -04:00
2021-08-08 21:08:16 -04:00
while ( ( x = _unbang . load ( ) ) ) {
2021-07-19 11:57:31 -04:00
2021-08-08 21:08:16 -04:00
_unbang . fetch_sub ( 1 ) ;
2021-07-19 11:57:31 -04:00
2021-12-15 14:26:37 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 unbanged \n " , index ( ) ) ) ;
2021-08-12 14:42:29 -04:00
if ( _launch_style = = Gate | | _launch_style = = Repeat ) {
2021-08-11 16:18:34 -04:00
switch ( _state ) {
case Running :
2021-12-15 13:03:47 -05:00
begin_stop ( true ) ;
2021-08-11 16:18:34 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 unbanged, now in WaitingToStop \n " , index ( ) ) ) ;
break ;
default :
/* didn't even get started */
2021-10-19 14:52:32 -04:00
shutdown ( ) ;
2021-08-11 16:18:34 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 unbanged, never started, now stopped \n " , index ( ) ) ) ;
}
2021-08-08 21:08:16 -04:00
}
}
}
2021-07-19 11:57:31 -04:00
2021-12-02 13:27:18 -05:00
pframes_t
Trigger : : maybe_compute_next_transition ( samplepos_t start_sample , Temporal : : Beats const & start , Temporal : : Beats const & end , pframes_t dest_offset , bool passthru )
2021-08-08 21:08:16 -04:00
{
2021-10-14 23:08:34 -04:00
using namespace Temporal ;
2021-12-11 23:05:22 -05:00
2021-12-02 13:27:18 -05:00
/* This should never be called by a stopped trigger */
assert ( _state ! = Stopped ) ;
2021-08-10 22:35:39 -04:00
/* In these states, we are not waiting for a transition */
2021-12-02 13:27:18 -05:00
if ( _state = = Running | | _state = = Stopping ) {
/* will cover everything */
2021-12-06 16:06:48 -05:00
return 0 ;
2021-08-10 22:35:39 -04:00
}
2021-10-25 14:53:27 -04:00
timepos_t transition_time ( BeatTime ) ;
2021-11-17 23:14:56 -05:00
TempoMap : : SharedPtr tmap ( TempoMap : : use ( ) ) ;
2021-11-29 23:50:19 -05:00
Temporal : : BBT_Time transition_bbt ;
2021-12-06 16:06:48 -05:00
pframes_t extra_offset = 0 ;
2021-12-18 16:11:54 -05:00
BBT_Offset q ( _quantization ) ;
2021-10-25 14:53:27 -04:00
/* XXX need to use global grid here is quantization == zero */
2021-07-19 11:57:31 -04:00
2021-12-02 13:27:18 -05:00
/* Given the value of @param start, determine, based on the
* quantization , the next time for a transition .
*/
2021-12-18 16:11:54 -05:00
if ( q . bars = = 0 ) {
Temporal : : Beats transition_beats = start . round_up_to_multiple ( Temporal : : Beats ( q . beats , q . ticks ) ) ;
2021-11-29 23:50:19 -05:00
transition_bbt = tmap - > bbt_at ( transition_beats ) ;
transition_time = timepos_t ( transition_beats ) ;
2021-08-08 21:08:16 -04:00
} else {
2021-11-29 23:50:19 -05:00
transition_bbt = tmap - > bbt_at ( timepos_t ( start ) ) ;
transition_bbt = transition_bbt . round_up_to_bar ( ) ;
2021-12-18 16:11:54 -05:00
transition_bbt . bars = ( transition_bbt . bars / q . bars ) * q . bars ;
2021-11-29 23:50:19 -05:00
transition_time = timepos_t ( tmap - > quarters_at ( transition_bbt ) ) ;
2021-08-08 21:08:16 -04:00
}
2021-07-19 11:57:31 -04:00
2021-12-18 16:11:54 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 quantized with %5 transition at %2, sb %3 eb %4 \n " , index ( ) , transition_time . beats ( ) , start , end , q ) ) ;
2021-11-29 23:50:19 -05:00
2021-12-02 13:27:18 -05:00
/* See if this time falls within the range of time given to us */
2021-10-25 14:53:27 -04:00
if ( transition_time . beats ( ) > = start & & transition_time < end ) {
2021-08-10 22:35:39 -04:00
2021-12-02 13:27:18 -05:00
/* transition time has arrived! let's figure out what're doing:
* stopping , starting , retriggering
*/
2021-10-25 14:53:27 -04:00
transition_samples = transition_time . samples ( ) ;
transition_beats = transition_time . beats ( ) ;
2021-07-19 11:57:31 -04:00
2021-10-26 19:34:06 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 in range, should start/stop at %2 aka %3 \n " , index ( ) , transition_samples , transition_beats ) ) ;
2021-12-02 13:27:18 -05:00
switch ( _state ) {
case WaitingToStop :
2021-08-08 21:08:16 -04:00
_state = Stopping ;
2021-08-31 13:53:24 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
2021-12-02 13:27:18 -05:00
/* trigger will reach it's end somewhere within this
* process cycle , so compute the number of samples it
* should generate .
*/
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 will stop somewhere in the middle of run() \n " , name ( ) ) ) ;
/* offset within the buffer(s) for output remains
unchanged , since we will write from the first
location corresponding to start
*/
break ;
case WaitingToStart :
2021-08-10 22:35:39 -04:00
retrigger ( ) ;
_state = Running ;
2021-12-15 12:21:28 -05:00
set_expected_end_sample ( tmap , transition_bbt ) ;
2021-11-29 23:50:19 -05:00
cerr < < " starting at " < < transition_bbt < < " bars " < < round ( _barcnt ) < < " end at " < < tmap - > bbt_walk ( transition_bbt , BBT_Offset ( round ( _barcnt ) , 0 , 0 ) ) < < " sample = " < < expected_end_sample < < endl ;
2021-08-31 13:53:24 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
2021-12-02 13:27:18 -05:00
/* trigger will start somewhere within this process
* cycle . Compute the sample offset where any audio
* should end up , and the number of samples it should generate .
*/
2021-12-06 16:06:48 -05:00
extra_offset = std : : max ( samplepos_t ( 0 ) , transition_samples - start_sample ) ;
2021-12-02 13:27:18 -05:00
if ( ! passthru ) {
/* XXX need to silence start of buffers up to dest_offset */
}
break ;
case WaitingForRetrigger :
2021-08-08 21:08:16 -04:00
retrigger ( ) ;
_state = Running ;
2021-12-15 12:21:28 -05:00
set_expected_end_sample ( tmap , transition_bbt ) ;
2021-11-29 23:50:19 -05:00
cerr < < " starting at " < < transition_bbt < < " bars " < < round ( _barcnt ) < < " end at " < < tmap - > bbt_walk ( transition_bbt , BBT_Offset ( round ( _barcnt ) , 0 , 0 ) ) < < " sample = " < < expected_end_sample < < endl ;
2021-08-31 13:53:24 -04:00
PropertyChanged ( ARDOUR : : Properties : : running ) ;
2021-12-02 13:27:18 -05:00
/* trigger is just running normally, and will fill
* buffers entirely .
*/
break ;
default :
fatal < < string_compose ( _ ( " programming error: %1 " ) , " impossible trigger state in ::maybe_compute_next_transition() " ) < < endmsg ;
abort ( ) ;
2021-08-10 22:35:39 -04:00
}
2021-12-02 13:27:18 -05:00
2021-08-10 22:35:39 -04:00
} else {
2021-12-02 13:27:18 -05:00
/* retrigger time has not been reached, just continue
to play normally until then .
*/
2021-08-08 21:08:16 -04:00
}
2021-07-19 11:57:31 -04:00
2021-12-06 16:06:48 -05:00
return extra_offset ;
2021-08-10 22:35:39 -04:00
}
2021-08-08 21:08:16 -04:00
/*--------------------*/
2021-07-17 21:01:10 -04:00
2021-12-21 15:37:47 -05:00
AudioTrigger : : AudioTrigger ( uint32_t n , TriggerBox & b )
2021-08-08 21:08:16 -04:00
: Trigger ( n , b )
2021-12-18 19:34:06 -05:00
, _stretcher ( 0 )
2021-08-13 00:59:04 -04:00
, _start_offset ( 0 )
2021-08-11 20:14:27 -04:00
, usable_length ( 0 )
2021-08-13 00:59:04 -04:00
, last_sample ( 0 )
2021-12-18 19:34:06 -05:00
, read_index ( 0 )
, process_index ( 0 )
, _legato_offset ( 0 )
, retrieved ( 0 )
2021-11-29 23:50:19 -05:00
, got_stretcher_padding ( false )
, to_pad ( 0 )
, to_drop ( 0 )
2021-08-08 21:08:16 -04:00
{
}
AudioTrigger : : ~ AudioTrigger ( )
{
2021-12-17 14:37:51 -05:00
drop_data ( ) ;
2021-11-11 17:10:33 -05:00
delete _stretcher ;
2021-07-20 00:37:17 -04:00
}
2021-07-17 15:21:45 -04:00
2021-12-15 12:21:28 -05:00
bool
AudioTrigger : : stretching ( ) const
{
2021-12-15 19:06:25 -05:00
return ( _apparent_tempo ! = .0 ) & & _stretchable ;
2021-12-15 12:21:28 -05:00
}
void
AudioTrigger : : set_expected_end_sample ( Temporal : : TempoMap : : SharedPtr const & tmap , Temporal : : BBT_Time const & transition_bbt )
{
if ( stretching ( ) ) {
expected_end_sample = tmap - > sample_at ( tmap - > bbt_walk ( transition_bbt , Temporal : : BBT_Offset ( round ( _barcnt ) , 0 , 0 ) ) ) ;
} else {
2021-12-16 12:37:56 -05:00
expected_end_sample = transition_samples + usable_length ;
2021-12-15 12:21:28 -05:00
}
}
2021-12-12 13:20:58 -05:00
SegmentDescriptor
AudioTrigger : : get_segment_descriptor ( ) const
{
SegmentDescriptor sd ;
sd . set_extent ( _start_offset , usable_length ) ;
sd . set_tempo ( Temporal : : Tempo ( _apparent_tempo , 4 ) ) ;
return sd ;
}
2021-09-05 01:19:47 -04:00
void
AudioTrigger : : startup ( )
{
Trigger : : startup ( ) ;
retrigger ( ) ;
}
2021-09-05 12:40:58 -04:00
void
AudioTrigger : : jump_start ( )
{
Trigger : : jump_start ( ) ;
retrigger ( ) ;
}
void
AudioTrigger : : jump_stop ( )
{
Trigger : : jump_stop ( ) ;
retrigger ( ) ;
}
2021-10-04 00:44:03 -04:00
double
AudioTrigger : : position_as_fraction ( ) const
{
if ( ! active ( ) ) {
return 0.0 ;
}
2021-12-15 12:21:28 -05:00
return process_index / ( double ) ( expected_end_sample - transition_samples ) ;
2021-10-04 00:44:03 -04:00
}
2021-08-31 18:46:19 -04:00
XMLNode &
AudioTrigger : : get_state ( void )
{
XMLNode & node ( Trigger : : get_state ( ) ) ;
node . set_property ( X_ ( " start " ) , timepos_t ( _start_offset ) ) ;
node . set_property ( X_ ( " length " ) , timepos_t ( usable_length ) ) ;
return node ;
}
int
2021-08-31 20:36:16 -04:00
AudioTrigger : : set_state ( const XMLNode & node , int version )
2021-08-31 18:46:19 -04:00
{
2021-08-31 20:36:16 -04:00
timepos_t t ;
2021-09-01 00:35:14 -04:00
if ( ! Trigger : : set_state ( node , version ) ) {
return - 1 ;
}
2021-08-31 20:36:16 -04:00
node . get_property ( X_ ( " start " ) , t ) ;
_start_offset = t . samples ( ) ;
node . get_property ( X_ ( " length " ) , t ) ;
usable_length = t . samples ( ) ;
last_sample = _start_offset + usable_length ;
2021-08-31 18:46:19 -04:00
return 0 ;
}
2021-07-20 00:37:17 -04:00
void
2021-08-31 13:53:24 -04:00
AudioTrigger : : set_start ( timepos_t const & s )
2021-08-13 00:59:04 -04:00
{
_start_offset = s . samples ( ) ;
}
void
2021-08-31 13:53:24 -04:00
AudioTrigger : : set_end ( timepos_t const & e )
2021-08-13 00:59:04 -04:00
{
2021-10-06 17:20:52 -04:00
assert ( ! data . empty ( ) ) ;
2021-10-05 18:01:52 -04:00
set_length ( timecnt_t ( e . samples ( ) - _start_offset , timepos_t ( _start_offset ) ) ) ;
2021-08-13 00:59:04 -04:00
}
2021-08-31 13:53:24 -04:00
void
AudioTrigger : : set_legato_offset ( timepos_t const & offset )
{
_legato_offset = offset . samples ( ) ;
}
timepos_t
AudioTrigger : : current_pos ( ) const
{
2021-11-29 23:50:19 -05:00
return timepos_t ( process_index ) ;
2021-08-31 13:53:24 -04:00
}
2021-08-11 20:25:46 -04:00
void
AudioTrigger : : set_usable_length ( )
{
if ( ! _region ) {
return ;
}
switch ( _launch_style ) {
case Repeat :
break ;
default :
2021-12-17 14:37:51 -05:00
usable_length = data . length ;
2021-08-13 00:59:04 -04:00
last_sample = _start_offset + usable_length ;
2021-08-11 20:25:46 -04:00
return ;
}
2021-12-18 16:11:54 -05:00
Temporal : : BBT_Offset q ( _quantization ) ;
if ( q = = Temporal : : BBT_Offset ( ) ) {
2021-12-17 14:37:51 -05:00
usable_length = data . length ;
2021-08-13 00:59:04 -04:00
last_sample = _start_offset + usable_length ;
2021-08-11 20:25:46 -04:00
return ;
}
/* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */
2021-12-18 16:11:54 -05:00
timecnt_t len ( Temporal : : Beats ( q . beats , q . ticks ) , timepos_t ( Temporal : : Beats ( ) ) ) ;
2021-08-11 20:25:46 -04:00
usable_length = len . samples ( ) ;
2021-08-13 00:59:04 -04:00
last_sample = _start_offset + usable_length ;
2021-12-15 16:58:24 -05:00
2021-12-17 14:37:51 -05:00
// std::cerr << name() << " SUL ul " << usable_length << " of " << data.length << " so " << _start_offset << " ls " << last_sample << std::endl;
2021-08-11 20:25:46 -04:00
}
2021-11-11 17:10:33 -05:00
void
AudioTrigger : : set_length ( timecnt_t const & newlen )
{
/* XXX what? */
}
2021-08-13 00:59:04 -04:00
timepos_t
2021-08-06 15:03:28 -04:00
AudioTrigger : : current_length ( ) const
{
if ( _region ) {
2021-12-17 14:37:51 -05:00
return timepos_t ( data . length ) ;
2021-08-06 15:03:28 -04:00
}
2021-08-13 00:59:04 -04:00
return timepos_t ( Temporal : : BeatTime ) ;
2021-08-06 15:03:28 -04:00
}
2021-08-13 00:59:04 -04:00
timepos_t
2021-08-06 15:03:28 -04:00
AudioTrigger : : natural_length ( ) const
{
if ( _region ) {
2021-08-13 00:59:04 -04:00
return timepos_t : : from_superclock ( _region - > length ( ) . magnitude ( ) ) ;
2021-08-06 15:03:28 -04:00
}
2021-08-13 00:59:04 -04:00
return timepos_t ( Temporal : : BeatTime ) ;
2021-08-06 15:03:28 -04:00
}
2021-07-19 11:57:31 -04:00
int
2021-11-10 10:45:38 -05:00
AudioTrigger : : set_region_threaded ( boost : : shared_ptr < Region > r )
2021-07-19 11:57:31 -04:00
{
2021-12-17 21:22:44 -05:00
assert ( ! active ( ) ) ;
2021-11-11 17:10:33 -05:00
2021-07-19 11:57:31 -04:00
boost : : shared_ptr < AudioRegion > ar = boost : : dynamic_pointer_cast < AudioRegion > ( r ) ;
2021-12-17 21:22:44 -05:00
if ( r & & ! ar ) {
2021-07-19 11:57:31 -04:00
return - 1 ;
}
set_region_internal ( r ) ;
2021-12-17 21:22:44 -05:00
if ( ! r ) {
/* unset */
return 0 ;
}
2021-10-07 10:03:46 -04:00
load_data ( ar ) ;
2021-11-11 17:10:33 -05:00
determine_tempo ( ) ;
setup_stretcher ( ) ;
2021-12-15 14:26:37 -05:00
set_usable_length ( ) ;
2021-07-19 11:57:31 -04:00
2021-10-07 10:03:46 -04:00
PropertyChanged ( ARDOUR : : Properties : : name ) ;
2021-10-06 17:20:52 -04:00
2021-10-07 10:03:46 -04:00
return 0 ;
}
void
2021-11-11 17:10:33 -05:00
AudioTrigger : : determine_tempo ( )
2021-10-07 10:03:46 -04:00
{
using namespace Temporal ;
2021-10-06 17:20:52 -04:00
2021-12-11 23:05:22 -05:00
/* check the name to see if there's a (heuristically obvious) hint
* about the tempo .
2021-10-05 23:09:16 -04:00
*/
2021-08-06 15:03:28 -04:00
2021-12-11 23:05:22 -05:00
string str = _region - > name ( ) ;
string : : size_type bi ;
string : : size_type ni ;
double text_tempo = - 1. ;
2021-12-13 11:20:32 -05:00
std : : cerr < < " Determine tempo for " < < name ( ) < < std : : endl ;
2021-12-11 23:05:22 -05:00
if ( ( ( bi = str . find ( " bpm " ) ) ! = string : : npos ) | |
( ( bi = str . find ( " BPM " ) ) ! = string : : npos ) ) {
string sub ( str . substr ( 0 , bi ) ) ;
if ( ( ni = sub . find_last_of ( " 0123456789.,_- " ) ) ! = string : : npos ) {
int nni = ni ; /* ni is unsigned, nni is signed */
while ( nni > = 0 ) {
if ( ! isdigit ( sub [ nni ] ) & &
( sub [ nni ] ! = ' . ' ) & &
( sub [ nni ] ! = ' , ' ) ) {
break ;
}
- - nni ;
}
if ( nni > 0 ) {
std : : stringstream p ( sub . substr ( nni + 1 ) ) ;
p > > text_tempo ;
if ( ! p ) {
text_tempo = - 1. ;
} else {
_apparent_tempo = text_tempo ;
std : : cerr < < " from filename, tempo = " < < _apparent_tempo < < std : : endl ;
}
}
}
}
2021-10-05 18:01:52 -04:00
TempoMap : : SharedPtr tm ( TempoMap : : use ( ) ) ;
2021-10-06 17:20:52 -04:00
2021-12-11 23:05:22 -05:00
/* We don't have too many good choices here. Triggers can fire at any
* time , so there ' s no special place on the tempo map that we can use
* to get the meter from and thus compute an apparent bar count for
* this region . Our solution for now : just use the first meter .
*/
TempoMetric const & metric ( tm - > metric_at ( timepos_t ( AudioTime ) ) ) ;
if ( text_tempo < 0 ) {
2021-10-06 17:20:52 -04:00
breakfastquay : : MiniBPM mbpm ( _box . session ( ) . sample_rate ( ) ) ;
mbpm . setBPMRange ( metric . tempo ( ) . quarter_notes_per_minute ( ) * 0.75 , metric . tempo ( ) . quarter_notes_per_minute ( ) * 1.5 ) ;
2021-10-07 10:03:46 -04:00
2021-12-17 14:37:51 -05:00
_apparent_tempo = mbpm . estimateTempoOfSamples ( data [ 0 ] , data . length ) ;
2021-11-11 17:10:33 -05:00
if ( _apparent_tempo = = 0.0 ) {
2021-10-11 19:56:31 -04:00
/* no apparent tempo, just return since we'll use it as-is */
2021-12-13 11:20:32 -05:00
std : : cerr < < " Could not determine tempo for " < < name ( ) < < std : : endl ;
2021-10-11 19:56:31 -04:00
return ;
}
2021-11-11 17:10:33 -05:00
2021-12-17 14:37:51 -05:00
cerr < < name ( ) < < " Estimated bpm " < < _apparent_tempo < < " from " < < ( double ) data . length / _box . session ( ) . sample_rate ( ) < < " seconds \n " ;
2021-10-06 17:20:52 -04:00
}
2021-12-17 14:37:51 -05:00
const double seconds = ( double ) data . length / _box . session ( ) . sample_rate ( ) ;
2021-12-11 23:05:22 -05:00
const double quarters = ( seconds / 60. ) * _apparent_tempo ;
_barcnt = quarters / metric . meter ( ) . divisions_per_bar ( ) ;
2021-12-15 17:39:28 -05:00
/* now check the determined tempo and force it to a value that gives us
an integer bar / quarter count . This is a heuristic that tries to
avoid clips that slightly over - or underrun a quantization point ,
resulting in small or larger gaps in output if they are repeating .
*/
2021-12-15 16:58:03 -05:00
if ( ( _apparent_tempo ! = 0. ) & & ( rint ( _barcnt ) ! = _barcnt ) ) {
/* fractional barcnt */
int intquarters = floor ( quarters ) ;
double at = _apparent_tempo ;
_apparent_tempo = intquarters / ( seconds / 60. ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " adjusted barcnt of %1 and q = %2 to %3, old %4 new at = %5 \n " , _barcnt , quarters , intquarters , at , _apparent_tempo ) ) ;
}
2021-10-06 17:20:52 -04:00
/* use initial tempo in map (assumed for now to be the only one */
2021-10-07 10:03:46 -04:00
const samplecnt_t one_bar = tm - > bbt_duration_at ( timepos_t ( AudioTime ) , BBT_Offset ( 1 , 0 , 0 ) ) . samples ( ) ;
2021-10-05 18:01:52 -04:00
2021-12-13 11:20:32 -05:00
cerr < < " tempo: " < < _apparent_tempo < < endl ;
2021-10-07 10:03:46 -04:00
cerr < < " one bar in samples: " < < one_bar < < endl ;
cerr < < " barcnt = " < < round ( _barcnt ) < < endl ;
}
2021-08-05 18:20:37 -04:00
2021-12-15 20:15:58 -05:00
void
AudioTrigger : : io_change ( )
{
if ( _stretcher ) {
setup_stretcher ( ) ;
}
}
/* This exists so that we can play with the value easily. Currently, 1024 seems as good as any */
2021-11-29 23:50:19 -05:00
static const samplecnt_t rb_blocksize = 1024 ;
2021-10-07 10:03:46 -04:00
void
2021-11-11 17:10:33 -05:00
AudioTrigger : : setup_stretcher ( )
2021-10-07 10:03:46 -04:00
{
2021-11-11 17:10:33 -05:00
using namespace RubberBand ;
using namespace Temporal ;
2021-10-07 10:03:46 -04:00
if ( ! _region ) {
return ;
}
2021-11-11 17:10:33 -05:00
boost : : shared_ptr < AudioRegion > ar ( boost : : dynamic_pointer_cast < AudioRegion > ( _region ) ) ;
2021-12-15 20:15:58 -05:00
const uint32_t nchans = std : : min ( _box . input_streams ( ) . n_audio ( ) , ar - > n_channels ( ) ) ;
2021-11-11 17:10:33 -05:00
/* XXX maybe get some of these options from region properties (when/if we have them) ? */
RubberBandStretcher : : Options options = RubberBandStretcher : : Option ( RubberBandStretcher : : OptionProcessRealTime |
2021-11-29 23:50:19 -05:00
RubberBandStretcher : : OptionTransientsCrisp ) ;
2021-11-11 17:10:33 -05:00
2021-12-15 20:15:58 -05:00
delete _stretcher ;
_stretcher = new RubberBandStretcher ( _box . session ( ) . sample_rate ( ) , nchans , options , 1.0 , 1.0 ) ;
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
_stretcher - > setMaxProcessSize ( rb_blocksize ) ;
2021-07-19 11:57:31 -04:00
}
void
AudioTrigger : : drop_data ( )
{
2021-12-17 14:37:51 -05:00
for ( auto & d : data ) {
delete [ ] d ;
2021-07-19 11:57:31 -04:00
}
data . clear ( ) ;
}
int
2021-10-26 19:34:06 -04:00
2021-07-19 11:57:31 -04:00
AudioTrigger : : load_data ( boost : : shared_ptr < AudioRegion > ar )
{
const uint32_t nchans = ar - > n_channels ( ) ;
2021-12-17 14:37:51 -05:00
data . length = ar - > length_samples ( ) ;
2021-07-19 11:57:31 -04:00
2021-08-11 20:14:27 -04:00
/* if usable length was already set, only adjust it if it is too large */
2021-12-17 14:37:51 -05:00
if ( ! usable_length | | usable_length > data . length ) {
usable_length = data . length ;
2021-08-13 00:59:04 -04:00
last_sample = _start_offset + usable_length ;
2021-08-11 20:14:27 -04:00
}
2021-07-19 11:57:31 -04:00
drop_data ( ) ;
try {
for ( uint32_t n = 0 ; n < nchans ; + + n ) {
2021-12-17 14:37:51 -05:00
data . push_back ( new Sample [ data . length ] ) ;
ar - > read ( data [ n ] , 0 , data . length , n ) ;
2021-07-19 11:57:31 -04:00
}
2021-08-31 13:53:24 -04:00
set_name ( ar - > name ( ) ) ;
2021-07-19 11:57:31 -04:00
} catch ( . . . ) {
drop_data ( ) ;
return - 1 ;
}
return 0 ;
}
2021-07-17 15:21:45 -04:00
void
2021-07-21 01:04:11 -04:00
AudioTrigger : : retrigger ( )
2021-07-17 15:21:45 -04:00
{
2021-08-31 13:53:24 -04:00
read_index = _start_offset + _legato_offset ;
2021-11-29 23:50:19 -05:00
process_index = 0 ;
retrieved = 0 ;
2021-09-05 12:40:58 -04:00
_legato_offset = 0 ; /* used one time only */
2021-11-11 17:10:33 -05:00
_stretcher - > reset ( ) ;
2021-11-29 23:50:19 -05:00
got_stretcher_padding = false ;
to_pad = 0 ;
to_drop = 0 ;
2021-09-05 01:19:47 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 retriggered to %2 \n " , _index , read_index ) ) ;
2021-07-19 11:57:31 -04:00
}
2021-11-29 23:50:19 -05:00
pframes_t
2021-12-02 13:27:18 -05:00
AudioTrigger : : run ( BufferSet & bufs , samplepos_t start_sample , samplepos_t end_sample , Temporal : : Beats const & start , Temporal : : Beats const & end , pframes_t nframes , pframes_t dest_offset , bool passthru , double bpm )
2021-07-17 15:21:45 -04:00
{
2021-08-10 22:35:39 -04:00
boost : : shared_ptr < AudioRegion > ar = boost : : dynamic_pointer_cast < AudioRegion > ( _region ) ;
2021-12-15 20:15:58 -05:00
/* We do not modify the I/O of our parent route, so we process only min (bufs.n_audio(),region.channels()) */
const uint32_t nchans = std : : min ( bufs . count ( ) . n_audio ( ) , ar - > n_channels ( ) ) ;
2021-11-17 23:14:56 -05:00
const pframes_t orig_nframes = nframes ;
2021-11-29 23:50:19 -05:00
int avail = 0 ;
BufferSet & scratch ( _box . session ( ) . get_scratch_buffers ( ChanCount ( DataType : : AUDIO , nchans ) ) ) ;
2021-12-03 09:31:45 -05:00
std : : vector < Sample * > bufp ( nchans ) ;
2021-12-15 12:21:28 -05:00
const bool do_stretch = stretching ( ) ;
2021-08-07 20:53:49 -04:00
2021-12-02 13:27:18 -05:00
/* see if we're going to start or stop or retrigger in this run() call */
2021-12-06 16:06:48 -05:00
pframes_t extra_offset = maybe_compute_next_transition ( start_sample , start , end , dest_offset , passthru ) ;
nframes - = extra_offset ;
dest_offset + = extra_offset ;
2021-12-02 13:27:18 -05:00
2021-12-17 13:03:09 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 after checking for transition, state = %2, will stretch %3 \n " , name ( ) , enum_2_string ( _state ) , do_stretch ) ) ;
2021-12-02 13:27:18 -05:00
switch ( _state ) {
case Stopped :
case WaitingForRetrigger :
case WaitingToStart :
/* did everything we could do */
return nframes ;
case Running :
case WaitingToStop :
case Stopping :
/* stuff to do */
break ;
}
2021-07-17 15:21:45 -04:00
2021-11-29 23:50:19 -05:00
/* We use session scratch buffers for both padding the start of the
* input to RubberBand , and to hold the output . Because of this dual
* purpose , we use a generic variable name ( ' bufp ' ) to refer to them .
*/
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
for ( uint32_t chn = 0 ; chn < bufs . count ( ) . n_audio ( ) ; + + chn ) {
bufp [ chn ] = scratch . get_audio ( chn ) . data ( ) ;
2021-11-11 17:10:33 -05:00
}
2021-11-29 23:50:19 -05:00
/* tell the stretcher what we are doing for this ::run() call */
2021-11-11 17:10:33 -05:00
2021-12-15 12:21:28 -05:00
if ( do_stretch ) {
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
const double stretch = _apparent_tempo / bpm ;
_stretcher - > setTimeRatio ( stretch ) ;
2021-11-11 17:10:33 -05:00
2021-12-17 13:03:09 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " clip tempo %1 bpm %2 ratio %3%4 \n " , _apparent_tempo , bpm , std : : setprecision ( 6 ) , stretch ) ) ;
2021-11-29 23:50:19 -05:00
if ( ( avail = _stretcher - > available ( ) ) < 0 ) {
error < < _ ( " Could not configure rubberband stretcher " ) < < endmsg ;
return - 1 ;
}
/* We are using Rubberband in realtime mode, but this mdoe of
* operation has some issues . The first is that it will
* generate a certain number of samples of output at the start
* that are not based on the input , due to processing latency .
*
* In this context , we don ' t care about this output , because we
* have all the data available from the outset , and we can just
* wait until this " latency " period is over . So we will feed
* an initial chunk of data to the stretcher , and then throw
* away the corresponding data on the output .
*
* This code is modelled on the code for rubberband ( 1 ) , part of
* the rubberband software .
*/
if ( ! got_stretcher_padding ) {
to_pad = _stretcher - > getLatency ( ) ;
to_drop = to_pad ;
got_stretcher_padding = true ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 requires %2 padding %3 \n " , name ( ) , to_pad ) ) ;
}
while ( to_pad > 0 ) {
const samplecnt_t limit = std : : min ( ( samplecnt_t ) scratch . get_audio ( 0 ) . capacity ( ) , to_pad ) ;
for ( uint32_t chn = 0 ; chn < nchans ; + + chn ) {
for ( samplecnt_t n = 0 ; n < limit ; + + n ) {
bufp [ chn ] [ n ] = 0.f ;
}
}
2021-12-03 09:31:45 -05:00
_stretcher - > process ( & bufp [ 0 ] , limit , false ) ;
2021-11-29 23:50:19 -05:00
to_pad - = limit ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 padded %2 left %3 \n " , name ( ) , limit , to_pad ) ) ;
}
2021-11-11 17:10:33 -05:00
}
2021-08-10 22:35:39 -04:00
while ( nframes ) {
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
pframes_t to_stretcher ;
pframes_t from_stretcher ;
2021-07-17 15:21:45 -04:00
2021-12-15 12:21:28 -05:00
if ( do_stretch ) {
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
if ( read_index < last_sample ) {
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
/* still have data to push into the stretcher */
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
to_stretcher = ( pframes_t ) std : : min ( samplecnt_t ( rb_blocksize ) , ( last_sample - read_index ) ) ;
const bool at_end = ( to_stretcher < rb_blocksize ) ;
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
while ( ( pframes_t ) avail < nframes & & ( read_index < last_sample ) ) {
/* keep feeding the stretcher in chunks of "to_stretcher",
* until there ' s nframes of data available , or we reach
* the end of the region
*/
2021-12-03 09:31:45 -05:00
std : : vector < Sample * > in ( nchans ) ;
2021-11-29 23:50:19 -05:00
for ( uint32_t chn = 0 ; chn < nchans ; + + chn ) {
in [ chn ] = data [ chn ] + read_index ;
}
2021-11-11 17:10:33 -05:00
2021-12-15 20:18:18 -05:00
/* Note: RubberBandStretcher's process() and retrieve() API's accepts Sample**
* as their first argument . This code may appear to only be processing the first
* channel , but actually processes them all in one pass .
*/
2021-12-03 09:31:45 -05:00
_stretcher - > process ( & in [ 0 ] , to_stretcher , at_end ) ;
2021-11-29 23:50:19 -05:00
read_index + = to_stretcher ;
avail = _stretcher - > available ( ) ;
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
if ( to_drop & & avail ) {
samplecnt_t this_drop = std : : min ( std : : min ( ( samplecnt_t ) avail , to_drop ) , ( samplecnt_t ) scratch . get_audio ( 0 ) . capacity ( ) ) ;
2021-12-03 09:31:45 -05:00
_stretcher - > retrieve ( & bufp [ 0 ] , this_drop ) ;
2021-11-29 23:50:19 -05:00
to_drop - = this_drop ;
avail = _stretcher - > available ( ) ;
}
// DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 process %2 at-end %3 avail %4 of %5\n", name(), to_stretcher, at_end, avail, nframes));
2021-11-11 17:10:33 -05:00
}
2021-11-29 23:50:19 -05:00
/* we've fed the stretcher enough data to have
* ( at least ) nframes of output available .
*/
from_stretcher = nframes ;
} else {
2021-11-17 23:14:56 -05:00
2021-11-29 23:50:19 -05:00
/* finished delivering data to stretcher, but may have not yet retrieved it all */
avail = _stretcher - > available ( ) ;
from_stretcher = ( pframes_t ) std : : min ( ( pframes_t ) nframes , ( pframes_t ) avail ) ;
2021-11-11 17:10:33 -05:00
}
2021-11-17 23:14:56 -05:00
2021-11-29 23:50:19 -05:00
/* fetch the stretch */
2021-11-17 23:14:56 -05:00
2021-12-03 09:31:45 -05:00
retrieved + = _stretcher - > retrieve ( & bufp [ 0 ] , from_stretcher ) ;
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
if ( read_index > = last_sample ) {
if ( transition_samples + retrieved > expected_end_sample ) {
from_stretcher = std : : min ( ( samplecnt_t ) from_stretcher , ( retrieved - ( expected_end_sample - transition_samples ) ) ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 total retrieved data %2 exceeds theoretical size %3, truncate from_stretcher to %4 \n " , expected_end_sample - transition_samples , from_stretcher ) ) ;
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
if ( from_stretcher = = 0 ) {
break ;
}
}
}
2021-11-11 17:10:33 -05:00
2021-11-29 23:50:19 -05:00
} else {
/* no stretch */
from_stretcher = ( pframes_t ) std : : min ( ( samplecnt_t ) nframes , ( last_sample - read_index ) ) ;
2021-11-11 17:10:33 -05:00
}
2021-11-29 23:50:19 -05:00
// DEBUG_TRACE (DEBUG::Triggers, string_compose ("%1 ready with %2 ri %3 ls %4, will write %5\n", name(), avail, read_index, last_sample, from_stretcher));
2021-11-11 17:10:33 -05:00
/* deliver to buffers */
for ( uint32_t chn = 0 ; chn < bufs . count ( ) . n_audio ( ) ; + + chn ) {
2021-08-06 23:26:50 -04:00
2021-12-21 15:37:47 -05:00
uint32_t channel = chn % data . size ( ) ;
2021-12-17 11:50:50 -05:00
AudioBuffer & buf ( bufs . get_audio ( chn ) ) ;
2021-12-15 12:21:28 -05:00
Sample * src = do_stretch ? bufp [ channel ] : ( data [ channel ] + read_index ) ;
2021-08-11 16:18:34 -04:00
2021-11-29 23:50:19 -05:00
if ( ! passthru ) {
buf . read_from ( src , from_stretcher , dest_offset ) ;
2021-12-17 17:26:39 -05:00
if ( _gain ! = 1.0f ) {
2021-11-29 23:50:19 -05:00
buf . apply_gain ( _gain , from_stretcher ) ;
2021-10-19 14:52:32 -04:00
}
2021-08-10 22:35:39 -04:00
} else {
2021-12-17 17:26:39 -05:00
if ( _gain ! = 1.0f ) {
2021-11-29 23:50:19 -05:00
buf . accumulate_with_gain_from ( src , from_stretcher , _gain , dest_offset ) ;
2021-10-19 14:52:32 -04:00
} else {
2021-12-17 11:50:50 -05:00
buf . accumulate_from ( src , from_stretcher , dest_offset ) ;
2021-10-19 14:52:32 -04:00
}
2021-08-10 22:35:39 -04:00
}
}
2021-08-06 23:26:50 -04:00
2021-11-29 23:50:19 -05:00
process_index + = from_stretcher ;
2021-08-06 23:26:50 -04:00
2021-11-29 23:50:19 -05:00
/* Move read_index, in the case that we are not using a
* stretcher
*/
2021-11-17 23:14:56 -05:00
2021-12-15 12:21:28 -05:00
if ( ! do_stretch ) {
2021-11-29 23:50:19 -05:00
read_index + = from_stretcher ;
}
2021-08-07 17:08:26 -04:00
2021-11-29 23:50:19 -05:00
nframes - = from_stretcher ;
avail = _stretcher - > available ( ) ;
dest_offset + = from_stretcher ;
2021-10-07 00:40:37 -04:00
2021-11-29 23:50:19 -05:00
if ( read_index > = last_sample & & ( _apparent_tempo = = 0. | | avail < = 0 ) ) {
_state = Stopped ;
_loop_cnt + + ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 reached end, now stopped, retrieved %2, avail %3 \n " , index ( ) , retrieved , avail ) ) ;
break ;
}
}
2021-11-17 23:14:56 -05:00
2021-12-14 23:55:52 -05:00
if ( _state = = Stopped | | _state = = Stopping ) {
2021-11-17 23:14:56 -05:00
2021-12-15 14:26:37 -05:00
if ( ( _state = = Stopped ) & & ! _explicitly_stopped & & ( _launch_style = = Trigger : : Gate | | _launch_style = = Trigger : : Repeat ) ) {
2021-08-10 22:35:39 -04:00
2021-12-15 14:26:37 -05:00
jump_start ( ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 was stopped, repeat/gate ret \n " , index ( ) ) ) ;
2021-08-10 22:35:39 -04:00
2021-12-15 14:26:37 -05:00
} else {
2021-08-06 23:26:50 -04:00
2021-12-15 14:26:37 -05:00
if ( ( _launch_style ! = Repeat ) & & ( _launch_style ! = Gate ) & & ( _loop_cnt = = _follow_count ) ) {
2021-12-14 23:55:52 -05:00
2021-12-15 14:26:37 -05:00
/* have played the specified number of times, we're done */
2021-12-14 23:55:52 -05:00
2021-12-15 14:26:37 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 loop cnt %2 satisfied, now stopped \n " , index ( ) , _follow_count ) ) ;
shutdown ( ) ;
2021-12-14 23:55:52 -05:00
2021-08-10 22:35:39 -04:00
2021-12-15 14:26:37 -05:00
} else if ( _state = = Stopping ) {
2021-08-07 17:08:26 -04:00
2021-12-15 14:26:37 -05:00
/* did not reach the end of the data. Presumably
* another trigger was explicitly queued , and we
* stopped
*/
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 not at end, but ow stopped \n " , index ( ) ) ) ;
shutdown ( ) ;
} else {
/* reached the end, but we haven't done that enough
* times yet for a follow action / stop to take
* effect . Time to get played again .
*/
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 was stopping, now waiting to retrigger, loop cnt %2 fc %3 \n " , index ( ) , _loop_cnt , _follow_count ) ) ;
_state = WaitingToStart ;
retrigger ( ) ;
PropertyChanged ( ARDOUR : : Properties : : running ) ;
}
2021-11-29 23:50:19 -05:00
}
2021-08-11 16:18:34 -04:00
}
2021-11-29 23:50:19 -05:00
return orig_nframes - nframes ;
2021-07-17 15:21:45 -04:00
}
2021-08-08 21:08:16 -04:00
2021-11-04 16:00:16 -04:00
void
AudioTrigger : : reload ( BufferSet & , void * )
{
}
2021-10-20 18:19:09 -04:00
/*--------------------*/
2021-12-21 15:37:47 -05:00
MIDITrigger : : MIDITrigger ( uint32_t n , TriggerBox & b )
2021-10-20 18:19:09 -04:00
: Trigger ( n , b )
2021-11-19 16:50:50 -05:00
, data_length ( Temporal : : Beats ( ) )
, usable_length ( Temporal : : Beats ( ) )
, last_event_beats ( Temporal : : Beats ( ) )
2021-10-20 18:19:09 -04:00
, _start_offset ( 0 , 0 , 0 )
, _legato_offset ( 0 , 0 , 0 )
{
}
MIDITrigger : : ~ MIDITrigger ( )
{
}
2021-12-15 12:21:28 -05:00
void
MIDITrigger : : set_expected_end_sample ( Temporal : : TempoMap : : SharedPtr const & tmap , Temporal : : BBT_Time const & transition_bbt )
{
expected_end_sample = tmap - > sample_at ( tmap - > bbt_walk ( transition_bbt , Temporal : : BBT_Offset ( round ( _barcnt ) , 0 , 0 ) ) ) ;
}
2021-12-12 13:20:58 -05:00
SegmentDescriptor
MIDITrigger : : get_segment_descriptor ( ) const
{
SegmentDescriptor sd ;
boost : : shared_ptr < MidiRegion > mr = boost : : dynamic_pointer_cast < MidiRegion > ( _region ) ;
assert ( mr ) ;
sd . set_extent ( Temporal : : Beats ( ) , mr - > length ( ) . beats ( ) ) ;
/* we don't really have tempo information for MIDI yet */
sd . set_tempo ( Temporal : : Tempo ( 120 , 4 ) ) ;
return sd ;
}
2021-10-20 18:19:09 -04:00
void
MIDITrigger : : startup ( )
{
Trigger : : startup ( ) ;
retrigger ( ) ;
}
void
MIDITrigger : : jump_start ( )
{
Trigger : : jump_start ( ) ;
retrigger ( ) ;
}
void
MIDITrigger : : jump_stop ( )
{
Trigger : : jump_stop ( ) ;
retrigger ( ) ;
}
double
MIDITrigger : : position_as_fraction ( ) const
{
if ( ! active ( ) ) {
return 0.0 ;
}
2021-11-19 16:50:50 -05:00
Temporal : : DoubleableBeats db ( last_event_beats ) ;
2021-10-24 11:27:14 -04:00
2021-11-19 16:50:50 -05:00
double dl = db . to_double ( ) ;
double dr = data_length . to_double ( ) ;
2021-10-29 18:22:55 -04:00
2021-11-19 16:50:50 -05:00
return dl / dr ;
2021-10-20 18:19:09 -04:00
}
XMLNode &
MIDITrigger : : get_state ( void )
{
XMLNode & node ( Trigger : : get_state ( ) ) ;
node . set_property ( X_ ( " start " ) , start_offset ( ) ) ;
node . set_property ( X_ ( " length " ) , timepos_t ( usable_length ) ) ;
return node ;
}
int
MIDITrigger : : set_state ( const XMLNode & node , int version )
{
timepos_t t ;
if ( ! Trigger : : set_state ( node , version ) ) {
return - 1 ;
}
node . get_property ( X_ ( " start " ) , t ) ;
Temporal : : Beats b ( t . beats ( ) ) ;
/* XXX need to deal with bar offsets */
_start_offset = Temporal : : BBT_Offset ( 0 , b . get_beats ( ) , b . get_ticks ( ) ) ;
node . get_property ( X_ ( " length " ) , t ) ;
2021-11-19 16:50:50 -05:00
usable_length = t . beats ( ) ;
2021-10-20 18:19:09 -04:00
return 0 ;
}
void
MIDITrigger : : set_start ( timepos_t const & s )
{
/* XXX need to handle bar offsets */
Temporal : : Beats b ( s . beats ( ) ) ;
_start_offset = Temporal : : BBT_Offset ( 0 , b . get_beats ( ) , b . get_ticks ( ) ) ;
}
void
MIDITrigger : : set_end ( timepos_t const & e )
{
/* XXX need to handle bar offsets */
set_length ( timecnt_t ( e . beats ( ) - Temporal : : Beats ( _start_offset . beats , _start_offset . ticks ) , start_offset ( ) ) ) ;
}
void
MIDITrigger : : set_legato_offset ( timepos_t const & offset )
{
/* XXX need to handle bar offsets */
Temporal : : Beats b ( offset . beats ( ) ) ;
_legato_offset = Temporal : : BBT_Offset ( 0 , b . get_beats ( ) , b . get_ticks ( ) ) ;
}
timepos_t
2021-10-24 11:27:14 -04:00
MIDITrigger : : start_offset ( ) const
2021-10-20 18:19:09 -04:00
{
2021-10-24 11:27:14 -04:00
/* XXX single meter assumption */
Temporal : : Meter const & m = Temporal : : TempoMap : : use ( ) - > meter_at ( Temporal : : Beats ( 0 , 0 ) ) ;
return timepos_t ( m . to_quarters ( _start_offset ) ) ;
2021-10-20 18:19:09 -04:00
}
timepos_t
2021-10-24 11:27:14 -04:00
MIDITrigger : : current_pos ( ) const
2021-10-20 18:19:09 -04:00
{
2021-11-19 16:50:50 -05:00
return timepos_t ( last_event_beats ) ;
2021-10-20 18:19:09 -04:00
}
void
MIDITrigger : : set_length ( timecnt_t const & newlen )
{
2021-10-24 11:27:14 -04:00
2021-10-20 18:19:09 -04:00
}
void
MIDITrigger : : set_usable_length ( )
{
if ( ! _region ) {
return ;
}
switch ( _launch_style ) {
case Repeat :
break ;
default :
usable_length = data_length ;
return ;
}
2021-12-18 16:11:54 -05:00
Temporal : : BBT_Offset q ( _quantization ) ;
if ( q = = Temporal : : BBT_Offset ( ) ) {
2021-10-20 18:19:09 -04:00
usable_length = data_length ;
return ;
}
/* XXX MUST HANDLE BAR-LEVEL QUANTIZATION */
2021-12-18 16:11:54 -05:00
timecnt_t len ( Temporal : : Beats ( q . beats , q . ticks ) , timepos_t ( Temporal : : Beats ( ) ) ) ;
2021-11-19 16:50:50 -05:00
usable_length = len . beats ( ) ;
2021-10-20 18:19:09 -04:00
}
timepos_t
MIDITrigger : : current_length ( ) const
{
if ( _region ) {
return timepos_t ( data_length ) ;
}
return timepos_t ( Temporal : : BeatTime ) ;
}
timepos_t
MIDITrigger : : natural_length ( ) const
{
if ( _region ) {
return timepos_t : : from_ticks ( _region - > length ( ) . magnitude ( ) ) ;
}
return timepos_t ( Temporal : : BeatTime ) ;
}
int
2021-11-10 10:45:38 -05:00
MIDITrigger : : set_region_threaded ( boost : : shared_ptr < Region > r )
2021-10-20 18:19:09 -04:00
{
boost : : shared_ptr < MidiRegion > mr = boost : : dynamic_pointer_cast < MidiRegion > ( r ) ;
if ( ! mr ) {
return - 1 ;
}
set_region_internal ( r ) ;
2021-11-19 16:50:50 -05:00
set_name ( mr - > name ( ) ) ;
data_length = mr - > length ( ) . beats ( ) ;
2021-10-20 18:19:09 -04:00
set_length ( mr - > length ( ) ) ;
2021-11-19 16:50:50 -05:00
model = mr - > model ( ) ;
2021-10-20 18:19:09 -04:00
2021-11-19 16:50:50 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 loaded midi region, span is %2 \n " , name ( ) , data_length ) ) ;
2021-11-04 16:00:16 -04:00
2021-10-20 18:19:09 -04:00
PropertyChanged ( ARDOUR : : Properties : : name ) ;
return 0 ;
}
void
MIDITrigger : : retrigger ( )
{
/* XXX need to deal with bar offsets */
2021-10-24 11:27:14 -04:00
// const Temporal::BBT_Offset o = _start_offset + _legato_offset;
2021-11-19 16:50:50 -05:00
iter = model - > begin ( ) ;
2021-10-20 18:19:09 -04:00
_legato_offset = Temporal : : BBT_Offset ( ) ;
2021-11-19 16:50:50 -05:00
last_event_beats = Temporal : : Beats ( ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 retriggered to %2, ts = %3 \n " , _index , iter - > time ( ) , transition_beats ) ) ;
}
void
MIDITrigger : : reload ( BufferSet & , void * )
{
2021-10-20 18:19:09 -04:00
}
2021-11-29 23:50:19 -05:00
pframes_t
2021-12-02 13:27:18 -05:00
MIDITrigger : : run ( BufferSet & bufs , samplepos_t start_sample , samplepos_t end_sample ,
Temporal : : Beats const & start_beats , Temporal : : Beats const & end_beats ,
pframes_t nframes , pframes_t dest_offset , bool passthru , double bpm )
2021-10-20 18:19:09 -04:00
{
2021-10-24 11:27:14 -04:00
MidiBuffer & mb ( bufs . get_midi ( 0 ) ) ;
2021-11-19 16:50:50 -05:00
typedef Evoral : : Event < MidiModel : : TimeType > MidiEvent ;
const timepos_t region_start_time = _region - > start ( ) ;
const Temporal : : Beats region_start = region_start_time . beats ( ) ;
Temporal : : TempoMap : : SharedPtr tmap ( Temporal : : TempoMap : : use ( ) ) ;
2021-12-04 13:49:09 -05:00
samplepos_t last_event_samples = max_samplepos ;
2021-10-24 11:27:14 -04:00
2021-12-02 13:27:18 -05:00
/* see if we're going to start or stop or retrigger in this run() call */
dest_offset = maybe_compute_next_transition ( start_sample , start_beats , end_beats , dest_offset , passthru ) ;
switch ( _state ) {
case Stopped :
case WaitingForRetrigger :
case WaitingToStart :
return nframes ;
case Running :
case WaitingToStop :
case Stopping :
break ;
}
2021-11-29 23:50:19 -05:00
if ( ! passthru ) {
mb . clear ( ) ;
}
2021-12-02 13:27:18 -05:00
while ( iter ! = model - > end ( ) ) {
2021-10-24 11:27:14 -04:00
2021-11-19 16:50:50 -05:00
MidiEvent const & next_event ( * iter ) ;
2021-10-26 19:34:06 -04:00
2021-11-19 16:50:50 -05:00
/* Event times are in beats, relative to start of source
* file . We need to convert to region - relative time , and then
* a session timeline time , which is defined by the time at
* which we last transitioned ( in this case , to being active )
*/
2021-10-26 19:34:06 -04:00
2021-12-04 15:33:32 -05:00
2021-11-19 16:50:50 -05:00
const Temporal : : Beats effective_time = transition_beats + ( next_event . time ( ) - region_start ) ;
2021-10-26 19:34:06 -04:00
2021-11-19 16:50:50 -05:00
/* Now get samples */
2021-10-25 14:53:49 -04:00
2021-11-29 23:50:19 -05:00
const samplepos_t timeline_samples = tmap - > sample_at ( effective_time ) ;
2021-10-25 14:53:49 -04:00
2021-12-02 13:27:18 -05:00
if ( timeline_samples > = end_sample ) {
2021-11-19 16:50:50 -05:00
break ;
}
2021-10-29 18:22:55 -04:00
2021-11-19 16:50:50 -05:00
/* Now we have to convert to a position within the buffer we
* are writing to .
2021-12-02 13:27:18 -05:00
*
* There ' s a slight complication here , because both
* start_sample and dest_offset reflect an offset from the
* start of the buffer that our parent ( TriggerBox ) processor
* is handling in its own run ( ) method . start_sample may have
* been adjusted to reflect a previous Trigger ' s processing
* during this run cycle , and so has dest_offset .
2021-11-19 16:50:50 -05:00
*/
2021-10-29 18:22:55 -04:00
2021-12-02 13:27:18 -05:00
samplepos_t buffer_samples = timeline_samples - start_sample + dest_offset ;
2021-12-04 15:25:33 -05:00
last_event_samples = timeline_samples ;
2021-10-29 18:22:55 -04:00
2021-11-19 16:50:50 -05:00
const Evoral : : Event < MidiBuffer : : TimeType > ev ( Evoral : : MIDI_EVENT , buffer_samples , next_event . size ( ) , const_cast < uint8_t * > ( next_event . buffer ( ) ) , false ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " inserting %1 \n " , ev ) ) ;
mb . insert_event ( ev ) ;
tracker . track ( next_event . buffer ( ) ) ;
last_event_beats = next_event . time ( ) ;
2021-10-26 19:34:06 -04:00
2021-11-19 16:50:50 -05:00
+ + iter ;
2021-10-25 14:53:49 -04:00
}
2021-10-24 11:27:14 -04:00
2021-11-30 09:57:34 -05:00
2021-10-25 14:53:49 -04:00
if ( _state = = Stopping ) {
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 was stopping, now stopped \n " , index ( ) ) ) ;
2021-12-04 13:50:00 -05:00
tracker . resolve_notes ( mb , nframes - 1 ) ;
2021-10-24 11:27:14 -04:00
}
2021-12-02 13:27:18 -05:00
if ( iter = = model - > end ( ) ) {
2021-11-30 09:57:34 -05:00
2021-12-02 13:27:18 -05:00
/* We reached the end */
2021-11-30 09:57:34 -05:00
2021-12-02 13:27:18 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 reached end \n " , index ( ) ) ) ;
2021-11-30 09:57:34 -05:00
2021-12-04 13:49:09 -05:00
_loop_cnt + + ;
2021-11-30 09:57:34 -05:00
2021-12-04 13:49:09 -05:00
if ( _loop_cnt = = _follow_count ) {
/* have played the specified number of times, we're done */
2021-12-02 13:27:18 -05:00
2021-12-04 13:49:09 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 loop cnt %2 satisfied, now stopped \n " , index ( ) , _follow_count ) ) ;
shutdown ( ) ;
2021-12-02 13:27:18 -05:00
2021-12-04 13:49:09 -05:00
} else {
2021-12-02 13:27:18 -05:00
2021-12-04 13:49:09 -05:00
/* reached the end, but we haven't done that enough
* times yet for a follow action / stop to take
* effect . Time to get played again .
*/
2021-12-02 13:27:18 -05:00
2021-12-04 13:49:09 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 was stopping, now waiting to retrigger, loop cnt %2 fc %3 \n " , index ( ) , _loop_cnt , _follow_count ) ) ;
/* we will "restart" at the beginning of the
next iteration of the trigger .
*/
transition_beats = transition_beats + data_length ;
retrigger ( ) ;
_state = WaitingToStart ;
2021-11-30 09:57:34 -05:00
}
2021-12-02 13:27:18 -05:00
/* the time we processed spans from start to the last event */
2021-12-04 13:49:09 -05:00
if ( last_event_samples ! = max_samplepos ) {
nframes = ( last_event_samples - start_sample ) ;
} else {
/* all frames covered */
}
2021-12-02 13:27:18 -05:00
} else {
/* we didn't reach the end of the MIDI data, ergo we covered
the entire timespan passed into us .
*/
2021-11-30 09:57:34 -05:00
}
2021-11-29 23:50:19 -05:00
return nframes ;
2021-10-20 18:19:09 -04:00
}
2021-08-08 21:08:16 -04:00
/**************/
void
Trigger : : make_property_quarks ( )
{
2021-11-15 14:27:32 -05:00
Properties : : running . property_id = g_quark_from_static_string ( X_ ( " running " ) ) ;
2021-08-08 21:08:16 -04:00
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for running = %1 \n " , Properties : : running . property_id ) ) ;
2021-11-15 14:27:32 -05:00
Properties : : passthru . property_id = g_quark_from_static_string ( X_ ( " passthru " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for passthru = %1 \n " , Properties : : passthru . property_id ) ) ;
2021-12-14 10:32:06 -05:00
Properties : : follow_count . property_id = g_quark_from_static_string ( X_ ( " follow-count " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for follow_count = %1 \n " , Properties : : follow_count . property_id ) ) ;
2021-09-28 19:45:35 -04:00
Properties : : legato . property_id = g_quark_from_static_string ( X_ ( " legato " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for legato = %1 \n " , Properties : : legato . property_id ) ) ;
2021-12-14 14:09:43 -05:00
Properties : : velocity_effect . property_id = g_quark_from_static_string ( X_ ( " velocity-effect " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for velocity_effect = %1 \n " , Properties : : velocity_effect . property_id ) ) ;
Properties : : follow_action_probability . property_id = g_quark_from_static_string ( X_ ( " follow-action-probability " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for follow_action_probability = %1 \n " , Properties : : follow_action_probability . property_id ) ) ;
2021-09-28 19:45:35 -04:00
Properties : : use_follow . property_id = g_quark_from_static_string ( X_ ( " use-follow " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for use-follow = %1 \n " , Properties : : use_follow . property_id ) ) ;
2021-09-29 20:00:55 -04:00
Properties : : quantization . property_id = g_quark_from_static_string ( X_ ( " quantization " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for quantization = %1 \n " , Properties : : quantization . property_id ) ) ;
2021-10-01 20:39:06 -04:00
Properties : : launch_style . property_id = g_quark_from_static_string ( X_ ( " launch-style " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for quantization = %1 \n " , Properties : : launch_style . property_id ) ) ;
Properties : : follow_action0 . property_id = g_quark_from_static_string ( X_ ( " follow-action-0 " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for follow-action-0 = %1 \n " , Properties : : follow_action0 . property_id ) ) ;
Properties : : follow_action1 . property_id = g_quark_from_static_string ( X_ ( " follow-action-1 " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for follow-action-1 = %1 \n " , Properties : : follow_action1 . property_id ) ) ;
2021-12-17 13:40:05 -05:00
Properties : : gain . property_id = g_quark_from_static_string ( X_ ( " gain " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for gain = %1 \n " , Properties : : gain . property_id ) ) ;
2021-12-15 19:06:25 -05:00
Properties : : stretchable . property_id = g_quark_from_static_string ( X_ ( " stretchable " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for stretchable = %1 \n " , Properties : : stretchable . property_id ) ) ;
2021-12-17 13:39:38 -05:00
Properties : : isolated . property_id = g_quark_from_static_string ( X_ ( " isolated " ) ) ;
DEBUG_TRACE ( DEBUG : : Properties , string_compose ( " quark for isolated = %1 \n " , Properties : : isolated . property_id ) ) ;
2021-08-08 21:08:16 -04:00
}
2021-11-03 20:44:18 -04:00
const int32_t TriggerBox : : default_triggers_per_box = 8 ;
2021-10-05 23:09:16 -04:00
Temporal : : BBT_Offset TriggerBox : : _assumed_trigger_duration ( 4 , 0 , 0 ) ;
2021-10-19 14:52:32 -04:00
//TriggerBox::TriggerMidiMapMode TriggerBox::_midi_map_mode (TriggerBox::AbletonPush);
TriggerBox : : TriggerMidiMapMode TriggerBox : : _midi_map_mode ( TriggerBox : : SequentialNote ) ;
2021-10-08 22:35:25 -04:00
int TriggerBox : : _first_midi_note = 60 ;
2021-11-01 11:14:02 -04:00
std : : atomic < int32_t > TriggerBox : : _pending_scene ( - 1 ) ;
std : : atomic < int32_t > TriggerBox : : _active_scene ( - 1 ) ;
2021-11-10 17:55:58 -05:00
std : : atomic < int > TriggerBox : : active_trigger_boxes ( 0 ) ;
2021-11-10 10:45:38 -05:00
TriggerBoxThread * TriggerBox : : worker = 0 ;
2021-11-10 17:55:58 -05:00
PBD : : Signal0 < void > TriggerBox : : StopAllTriggers ;
2021-11-10 10:45:38 -05:00
void
TriggerBox : : init ( )
{
worker = new TriggerBoxThread ;
TriggerBoxThread : : init_request_pool ( ) ;
init_pool ( ) ;
}
2021-08-31 13:53:24 -04:00
2021-11-10 17:55:58 -05:00
void
TriggerBox : : start_transport_stop ( Session & s )
{
if ( active_trigger_boxes . load ( ) ) {
StopAllTriggers ( ) ; /* EMIT SIGNAL */
} else {
s . stop_transport_from_trigger ( ) ;
}
}
2021-08-08 21:08:16 -04:00
TriggerBox : : TriggerBox ( Session & s , DataType dt )
: Processor ( s , _ ( " TriggerBox " ) , Temporal : : BeatTime )
, _data_type ( dt )
2021-10-13 11:07:07 -04:00
, _order ( - 1 )
2021-09-04 12:37:36 -04:00
, explicit_queue ( 64 )
2021-12-06 10:57:06 -05:00
, _currently_playing ( 0 )
2021-09-05 01:19:47 -04:00
, _stop_all ( false )
2021-11-15 14:27:32 -05:00
, _pass_thru ( false )
2021-11-04 11:50:34 -04:00
, requests ( 1024 )
2021-08-08 21:08:16 -04:00
{
2021-12-13 18:24:13 -05:00
set_display_to_user ( false ) ;
2021-08-08 21:08:16 -04:00
/* default number of possible triggers. call ::add_trigger() to increase */
if ( _data_type = = DataType : : AUDIO ) {
2021-12-21 15:37:47 -05:00
for ( uint32_t n = 0 ; n < default_triggers_per_box ; + + n ) {
all_triggers . push_back ( boost : : make_shared < AudioTrigger > ( n , * this ) ) ;
2021-08-08 21:08:16 -04:00
}
2021-10-20 18:53:52 -04:00
} else {
2021-12-21 15:37:47 -05:00
for ( uint32_t n = 0 ; n < default_triggers_per_box ; + + n ) {
all_triggers . push_back ( boost : : make_shared < MIDITrigger > ( n , * this ) ) ;
2021-10-20 18:53:52 -04:00
}
2021-08-08 21:08:16 -04:00
}
2021-12-21 19:36:39 -05:00
while ( pending . size ( ) < all_triggers . size ( ) ) {
pending . push_back ( std : : atomic < Trigger * > ( 0 ) ) ;
}
2021-12-21 17:15:06 -05:00
2021-10-09 18:58:58 -04:00
Config - > ParameterChanged . connect_same_thread ( * this , boost : : bind ( & TriggerBox : : parameter_changed , this , _1 ) ) ;
2021-11-10 17:55:58 -05:00
StopAllTriggers . connect_same_thread ( stop_all_connection , boost : : bind ( & TriggerBox : : request_stop_all , this ) ) ;
2021-08-08 21:08:16 -04:00
}
2021-11-01 11:14:02 -04:00
void
TriggerBox : : scene_bang ( uint32_t n )
{
2021-11-01 14:11:34 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " scene bang on %1 for %2 \n " , n ) ) ;
2021-11-01 11:14:02 -04:00
_pending_scene = n ;
}
2021-12-21 17:15:06 -05:00
void
TriggerBox : : set_region ( uint32_t slot , boost : : shared_ptr < Region > region )
{
/* This is called from our worker thread */
2021-12-21 19:36:39 -05:00
Trigger * t ;
2021-12-21 17:15:06 -05:00
switch ( _data_type ) {
case DataType : : AUDIO :
2021-12-21 19:36:39 -05:00
t = new AudioTrigger ( slot , * this ) ;
2021-12-21 17:15:06 -05:00
break ;
case DataType : : MIDI :
2021-12-21 19:36:39 -05:00
t = new MIDITrigger ( slot , * this ) ;
2021-12-21 17:15:06 -05:00
break ;
}
t - > set_region_threaded ( region ) ;
/* XXX what happens if pending is already set? */
set_pending ( slot , t ) ;
}
void
2021-12-21 19:36:39 -05:00
TriggerBox : : set_pending ( uint32_t slot , Trigger * t )
{
all_triggers [ slot ] - > set_pending ( t ) ;
}
void
TriggerBox : : maybe_swap_pending ( uint32_t slot )
2021-12-21 17:15:06 -05:00
{
2021-12-22 00:35:59 -05:00
/* This is called synchronously with process() (i.e. in an RT process
thread ) and so it is impossible for any Triggers in this TriggerBox
to be invoked while this executes .
*/
2021-12-21 19:36:39 -05:00
Trigger * p = 0 ;
p = all_triggers [ slot ] - > swap_pending ( p ) ;
if ( p ) {
2021-12-22 00:35:59 -05:00
if ( p = = Trigger : : MagicClearPointerValue ) {
all_triggers [ slot ] - > clear_region ( ) ;
} else {
if ( all_triggers [ slot ] ) {
/* Put existing Trigger for this slot in the deletion queue */
// XXX _deletion_queue....;
}
2021-12-21 19:36:39 -05:00
2021-12-22 00:35:59 -05:00
all_triggers [ slot ] . reset ( p ) ;
TriggerSwapped ( slot ) ; /* EMIT SIGNAL */
}
2021-12-21 19:36:39 -05:00
}
2021-12-21 17:15:06 -05:00
}
2021-11-01 11:14:02 -04:00
void
TriggerBox : : scene_unbang ( uint32_t n )
{
}
void
TriggerBox : : maybe_find_scene_bang ( )
{
2021-11-03 20:44:18 -04:00
int32_t pending = _pending_scene . exchange ( - 1 ) ;
2021-11-01 11:14:02 -04:00
if ( pending > = 0 ) {
_active_scene = pending ;
}
}
void
TriggerBox : : clear_scene_bang ( )
{
( void ) _active_scene . exchange ( - 1 ) ;
}
2021-10-13 11:07:07 -04:00
void
TriggerBox : : set_order ( int32_t n )
{
_order = n ;
}
2021-09-04 12:37:36 -04:00
void
2021-12-21 15:37:47 -05:00
TriggerBox : : queue_explict ( uint32_t n )
2021-09-04 12:37:36 -04:00
{
2021-12-21 15:37:47 -05:00
assert ( n < all_triggers . size ( ) ) ;
explicit_queue . write ( & n , 1 ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " explicit queue %1, EQ = %2 \n " , n , explicit_queue . read_space ( ) ) ) ;
2021-09-05 01:19:47 -04:00
2021-12-06 10:57:06 -05:00
if ( _currently_playing ) {
_currently_playing - > unbang ( ) ;
2021-09-05 01:19:47 -04:00
}
2021-09-04 12:37:36 -04:00
}
2021-12-21 15:37:47 -05:00
TriggerPtr
2021-09-04 12:37:36 -04:00
TriggerBox : : get_next_trigger ( )
{
2021-12-21 15:37:47 -05:00
uint32_t n ;
2021-09-04 12:37:36 -04:00
2021-12-21 15:37:47 -05:00
if ( explicit_queue . read ( & n , 1 ) = = 1 ) {
TriggerPtr r = trigger ( n ) ;
2021-11-29 23:50:19 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " next trigger from explicit queue = %1 \n " , r - > index ( ) ) ) ;
2021-09-04 12:37:36 -04:00
return r ;
}
return 0 ;
}
2021-11-10 10:45:38 -05:00
void
2021-12-21 15:37:47 -05:00
TriggerBox : : set_from_selection ( uint32_t slot , boost : : shared_ptr < Region > region )
2021-10-24 11:25:11 -04:00
{
2021-10-26 19:34:06 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " load %1 into %2 \n " , region - > name ( ) , slot ) ) ;
2021-10-24 11:25:11 -04:00
if ( slot > = all_triggers . size ( ) ) {
2021-11-10 10:45:38 -05:00
return ;
2021-10-24 11:25:11 -04:00
}
2021-11-10 10:45:38 -05:00
all_triggers [ slot ] - > set_region ( region ) ;
2021-10-24 11:25:11 -04:00
}
2021-11-10 10:45:38 -05:00
void
2021-12-21 15:37:47 -05:00
TriggerBox : : set_from_path ( uint32_t slot , std : : string const & path )
2021-08-08 21:08:16 -04:00
{
2021-10-11 19:56:56 -04:00
if ( slot > = all_triggers . size ( ) ) {
2021-11-10 10:45:38 -05:00
return ;
2021-10-11 19:56:56 -04:00
}
2021-08-08 21:08:16 -04:00
2021-12-18 11:31:44 -05:00
const DataType source_type = SMFSource : : safe_midi_file_extension ( path ) ? DataType : : MIDI : DataType : : AUDIO ;
if ( source_type ! = _data_type ) {
error < < string_compose ( _ ( " Cannot use %1 files in %2 slots " ) ,
( ( source_type = = DataType : : MIDI ) ? " MIDI " : " audio " ) ,
( ( source_type = = DataType : : MIDI ) ? " audio " : " MIDI " ) ) < < endmsg ;
return ;
}
2021-08-08 21:08:16 -04:00
try {
2021-12-15 20:18:18 -05:00
ImportStatus status ;
status . total = 1 ;
status . quality = SrcBest ;
status . freeze = false ;
status . paths . push_back ( path ) ;
status . replace_existing_source = false ;
status . split_midi_channels = false ;
2021-12-18 10:29:11 -05:00
status . midi_track_name_source = ARDOUR : : SMFTrackName ;
2021-08-08 21:08:16 -04:00
2021-12-15 20:18:18 -05:00
_session . import_files ( status ) ;
if ( status . cancel ) {
error < < string_compose ( _ ( " Cannot create source from %1 " ) , path ) < < endmsg ;
2021-11-10 10:45:38 -05:00
return ;
2021-08-08 21:08:16 -04:00
}
2021-12-18 10:29:35 -05:00
if ( status . sources . empty ( ) ) {
error < < string_compose ( _ ( " Could not create source from %1 " ) , path ) < < endmsg ;
return ;
}
2021-08-08 21:08:16 -04:00
SourceList src_list ;
2021-12-15 20:18:18 -05:00
for ( auto & src : status . sources ) {
src_list . push_back ( src ) ;
2021-08-08 21:08:16 -04:00
}
PropertyList plist ;
plist . add ( Properties : : start , 0 ) ;
plist . add ( Properties : : length , src_list . front ( ) - > length ( ) ) ;
plist . add ( Properties : : name , basename_nosuffix ( path ) ) ;
plist . add ( Properties : : layer , 0 ) ;
plist . add ( Properties : : layering_index , 0 ) ;
2021-09-01 00:35:14 -04:00
boost : : shared_ptr < Region > the_region ( RegionFactory : : create ( src_list , plist , true ) ) ;
2021-08-08 21:08:16 -04:00
all_triggers [ slot ] - > set_region ( the_region ) ;
} catch ( std : : exception & e ) {
cerr < < " loading sample from " < < path < < " failed: " < < e . what ( ) < < endl ;
2021-11-10 10:45:38 -05:00
return ;
2021-08-08 21:08:16 -04:00
}
}
TriggerBox : : ~ TriggerBox ( )
{
}
2021-09-05 01:19:47 -04:00
void
TriggerBox : : request_stop_all ( )
{
2021-11-29 23:50:19 -05:00
_requests . stop_all = true ;
2021-09-05 01:19:47 -04:00
}
2021-08-08 21:08:16 -04:00
void
TriggerBox : : stop_all ( )
{
/* XXX needs to be done with mutex or via thread-safe queue */
2021-11-29 23:50:19 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , " stop-all request received \n " ) ;
2021-12-21 15:37:47 -05:00
for ( uint32_t n = 0 ; n < all_triggers . size ( ) ; + + n ) {
2021-11-29 23:50:19 -05:00
all_triggers [ n ] - > request_stop ( ) ;
2021-08-08 21:08:16 -04:00
}
2021-09-05 01:19:47 -04:00
2021-11-29 23:50:19 -05:00
_stop_all = true ;
2021-11-10 17:55:58 -05:00
2021-11-29 23:50:19 -05:00
explicit_queue . reset ( ) ;
2021-08-08 21:08:16 -04:00
}
void
TriggerBox : : drop_triggers ( )
{
Glib : : Threads : : RWLock : : WriterLock lm ( trigger_lock ) ;
2021-08-31 21:03:32 -04:00
all_triggers . clear ( ) ;
2021-08-08 21:08:16 -04:00
}
2021-12-21 15:37:47 -05:00
TriggerPtr
2021-08-08 21:08:16 -04:00
TriggerBox : : trigger ( Triggers : : size_type n )
{
Glib : : Threads : : RWLock : : ReaderLock lm ( trigger_lock ) ;
if ( n > = all_triggers . size ( ) ) {
return 0 ;
}
return all_triggers [ n ] ;
}
2021-10-08 15:37:29 -04:00
void
2021-10-10 19:35:15 -04:00
TriggerBox : : add_midi_sidechain ( std : : string const & name )
2021-10-08 15:37:29 -04:00
{
if ( ! _sidechain ) {
2021-10-10 19:35:15 -04:00
_sidechain . reset ( new SideChain ( _session , name + " -trig " ) ) ;
2021-10-08 15:37:29 -04:00
_sidechain - > activate ( ) ;
_sidechain - > input ( ) - > add_port ( " " , owner ( ) , DataType : : MIDI ) ; // add a port, don't connect.
2021-10-10 19:35:15 -04:00
boost : : shared_ptr < Port > p = _sidechain - > input ( ) - > nth ( 0 ) ;
if ( p ) {
p - > connect ( Config - > get_default_trigger_input_port ( ) ) ;
} else {
error < < _ ( " Could not create port for trigger side-chain " ) < < endmsg ;
}
2021-10-08 15:37:29 -04:00
}
}
2021-08-08 21:08:16 -04:00
bool
TriggerBox : : can_support_io_configuration ( const ChanCount & in , ChanCount & out )
{
2021-12-15 20:15:58 -05:00
/* if this is an audio trigger, let it be known that we have at least 1 audio output.
*/
if ( _data_type = = DataType : : AUDIO ) {
out . set_audio ( std : : max ( in . n_audio ( ) , 1U ) ) ;
}
/* if this is a MIDI trigger, let it be known that we have at least 1 MIDI output.
*/
2021-12-13 18:50:31 -05:00
if ( _data_type = = DataType : : MIDI ) {
2021-12-15 20:15:58 -05:00
out . set_midi ( std : : max ( in . n_midi ( ) , 1U ) ) ;
2021-08-08 21:08:16 -04:00
}
return true ;
}
bool
TriggerBox : : configure_io ( ChanCount in , ChanCount out )
{
2021-10-08 15:37:29 -04:00
if ( _sidechain ) {
_sidechain - > configure_io ( in , out ) ;
}
2021-12-15 20:15:58 -05:00
bool ret = Processor : : configure_io ( in , out ) ;
if ( ret ) {
2021-12-21 15:37:47 -05:00
for ( uint32_t n = 0 ; n < all_triggers . size ( ) ; + + n ) {
2021-12-15 20:15:58 -05:00
all_triggers [ n ] - > io_change ( ) ;
}
}
2021-12-16 08:47:10 -05:00
return ret ;
2021-08-08 21:08:16 -04:00
}
void
2021-12-21 15:37:47 -05:00
TriggerBox : : add_trigger ( TriggerPtr trigger )
2021-08-08 21:08:16 -04:00
{
Glib : : Threads : : RWLock : : WriterLock lm ( trigger_lock ) ;
all_triggers . push_back ( trigger ) ;
}
2021-10-08 22:35:25 -04:00
void
TriggerBox : : set_midi_map_mode ( TriggerMidiMapMode m )
{
_midi_map_mode = m ;
}
void
TriggerBox : : set_first_midi_note ( int n )
{
_first_midi_note = n ;
}
int
TriggerBox : : note_to_trigger ( int midi_note , int channel )
{
2021-10-13 11:07:07 -04:00
const int column = _order ;
2021-10-08 22:35:25 -04:00
int first_note ;
2021-10-13 11:07:07 -04:00
int top ;
2021-10-08 22:35:25 -04:00
switch ( _midi_map_mode ) {
case AbletonPush :
2021-10-13 11:07:07 -04:00
/* the top row of pads generate MIDI note 92, 93, 94 and so on.
Each lower row generates notes 8 below the one above it .
*/
top = 92 + column ;
for ( int row = 0 ; row < 8 ; + + row ) {
if ( midi_note = = top - ( row * 8 ) ) {
return row ;
}
}
return - 1 ;
break ;
2021-10-08 22:35:25 -04:00
case SequentialNote :
2021-10-19 14:52:32 -04:00
first_note = _first_midi_note + ( column * all_triggers . size ( ) ) ;
2021-10-08 22:35:25 -04:00
return midi_note - first_note ; /* direct access to row */
case ByMidiChannel :
first_note = 3 ;
break ;
default :
break ;
}
return midi_note ;
}
2021-08-08 21:08:16 -04:00
void
TriggerBox : : process_midi_trigger_requests ( BufferSet & bufs )
{
/* check MIDI port input buffers for triggers */
for ( BufferSet : : midi_iterator mi = bufs . midi_begin ( ) ; mi ! = bufs . midi_end ( ) ; + + mi ) {
MidiBuffer & mb ( * mi ) ;
for ( MidiBuffer : : iterator ev = mb . begin ( ) ; ev ! = mb . end ( ) ; + + ev ) {
if ( ! ( * ev ) . is_note ( ) ) {
continue ;
}
2021-10-08 22:35:25 -04:00
int trigger_number = note_to_trigger ( ( * ev ) . note ( ) , ( * ev ) . channel ( ) ) ;
2021-08-08 21:08:16 -04:00
2021-10-13 11:07:07 -04:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " note %1 received on %2, translated to trigger num %3 \n " , ( int ) ( * ev ) . note ( ) , ( int ) ( * ev ) . channel ( ) , trigger_number ) ) ;
2021-10-08 22:35:25 -04:00
if ( trigger_number < 0 ) {
/* not for us */
continue ;
}
2021-08-08 21:08:16 -04:00
2021-10-08 22:35:25 -04:00
if ( trigger_number > ( int ) all_triggers . size ( ) ) {
continue ;
}
2021-08-08 21:08:16 -04:00
2021-12-21 15:37:47 -05:00
TriggerPtr t = all_triggers [ trigger_number ] ;
2021-08-08 21:08:16 -04:00
2021-10-08 22:35:25 -04:00
if ( ! t ) {
continue ;
}
2021-08-08 21:08:16 -04:00
2021-10-08 22:35:25 -04:00
if ( ( * ev ) . is_note_on ( ) ) {
2021-08-08 21:08:16 -04:00
2021-10-19 14:52:32 -04:00
if ( t - > midi_velocity_effect ( ) ! = 0.0 ) {
/* if MVE is zero, MIDI velocity has no
impact on gain . If it is small , it
has a small effect on gain . As it
approaches 1.0 , it has full control
over the trigger gain .
*/
t - > set_gain ( 1.0 - ( t - > midi_velocity_effect ( ) * ( * ev ) . velocity ( ) / 127.f ) ) ;
}
2021-10-08 22:35:25 -04:00
t - > bang ( ) ;
2021-08-08 21:08:16 -04:00
2021-10-08 22:35:25 -04:00
} else if ( ( * ev ) . is_note_off ( ) ) {
2021-10-08 15:37:29 -04:00
2021-10-08 22:35:25 -04:00
t - > unbang ( ) ;
2021-08-08 21:08:16 -04:00
}
}
}
}
2021-11-15 14:27:32 -05:00
void
TriggerBox : : set_pass_thru ( bool yn )
{
2021-11-29 23:50:19 -05:00
_requests . pass_thru = yn ;
2021-11-15 14:27:32 -05:00
PropertyChanged ( Properties : : passthru ) ;
}
2021-08-08 21:08:16 -04:00
void
TriggerBox : : run ( BufferSet & bufs , samplepos_t start_sample , samplepos_t end_sample , double speed , pframes_t nframes , bool result_required )
{
2021-10-11 19:57:10 -04:00
/* XXX a test to check if we have no usable slots would be good
here . if so , we can just return .
*/
2021-11-29 23:50:19 -05:00
/* STEP ONE: are we actually active? */
if ( ! check_active ( ) ) {
return ;
2021-12-03 07:52:51 -05:00
}
2021-10-11 10:50:58 -04:00
2021-12-16 11:31:12 -05:00
_pass_thru = _requests . pass_thru . load ( ) ;
2021-11-29 23:50:19 -05:00
bool allstop = _requests . stop_all . exchange ( false ) ;
/* STEP TWO: if latency compensation tells us that we haven't really
* started yet , do nothing , because we can ' t make sense of a negative
* start sample time w . r . t the tempo map .
*/
2021-08-08 21:08:16 -04:00
if ( start_sample < 0 ) {
return ;
}
2021-11-29 23:50:19 -05:00
const pframes_t orig_nframes = nframes ;
/* STEP THREE: triggers in audio tracks need a MIDI sidechain to be
* able to receive inbound MIDI for triggering etc . This needs to run
* before anything else , since we may need data just received to launch
* a trigger ( or stop it )
*/
2021-10-08 15:37:29 -04:00
if ( _sidechain ) {
_sidechain - > run ( bufs , start_sample , end_sample , speed , nframes , true ) ;
}
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
/* STEP FOUR: handle any incoming requests from the GUI or other
* non - MIDI UIs
*/
2021-11-04 16:00:16 -04:00
process_requests ( bufs ) ;
2021-11-29 23:50:19 -05:00
/* STEP FIVE: handle any incoming MIDI requests
*/
2021-10-08 15:37:29 -04:00
process_midi_trigger_requests ( bufs ) ;
2021-09-04 12:37:36 -04:00
2021-11-29 23:50:19 -05:00
/* STEP SIX: if at this point there is an active cue, make it trigger
* our corresponding slot
*/
2021-11-01 14:11:34 -04:00
if ( _active_scene > = 0 ) {
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " tb noticed active scene %1 \n " , _active_scene ) ) ;
2021-11-03 20:44:18 -04:00
if ( _active_scene < ( int32_t ) all_triggers . size ( ) ) {
2021-12-17 13:39:38 -05:00
if ( ! all_triggers [ _active_scene ] - > scene_isolated ( ) ) {
all_triggers [ _active_scene ] - > bang ( ) ;
}
2021-11-01 14:11:34 -04:00
}
}
2021-11-29 23:50:19 -05:00
/* STEP SEVEN: let each slot process any individual state requests
*/
2021-09-04 12:37:36 -04:00
2021-12-21 15:37:47 -05:00
std : : vector < uint32_t > to_run ;
2021-08-08 21:08:16 -04:00
2021-12-21 15:37:47 -05:00
for ( uint32_t n = 0 ; n < all_triggers . size ( ) ; + + n ) {
2021-08-08 21:08:16 -04:00
all_triggers [ n ] - > process_state_requests ( ) ;
2021-09-05 01:19:47 -04:00
}
2021-08-08 21:08:16 -04:00
2021-12-22 00:35:59 -05:00
if ( _currently_playing & & _currently_playing - > state ( ) = = Trigger : : Stopped ) {
_currently_playing = 0 ;
}
for ( uint32_t n = 0 ; n < all_triggers . size ( ) ; + + n ) {
if ( all_triggers [ n ] ! = _currently_playing ) {
maybe_swap_pending ( n ) ;
}
}
2021-11-29 23:50:19 -05:00
/* STEP EIGHT: if there is no active slot, see if there any queued up
*/
2021-09-05 12:40:58 -04:00
2021-12-06 10:57:06 -05:00
if ( ! _currently_playing ) {
if ( ( _currently_playing = get_next_trigger ( ) ) ! = 0 ) {
2021-12-21 19:36:39 -05:00
maybe_swap_pending ( _currently_playing - > index ( ) ) ;
2021-12-06 10:57:06 -05:00
_currently_playing - > startup ( ) ;
PropertyChanged ( Properties : : currently_playing ) ;
2021-11-10 17:55:58 -05:00
active_trigger_boxes . fetch_add ( 1 ) ;
2021-08-08 21:08:16 -04:00
}
}
2021-11-29 23:50:19 -05:00
/* STEP NINE: if we've been told to stop all slots, do so
*/
if ( allstop ) {
2021-11-10 17:55:58 -05:00
stop_all ( ) ;
}
2021-11-29 23:50:19 -05:00
/* STEP TEN: nothing to do?
*/
2021-12-06 10:57:06 -05:00
if ( ! _currently_playing ) {
2021-08-08 21:08:16 -04:00
return ;
}
/* transport must be active for triggers */
if ( ! _session . transport_state_rolling ( ) ) {
2021-11-10 17:55:58 -05:00
_session . start_transport_from_trigger ( ) ;
2021-08-08 21:08:16 -04:00
}
2021-11-29 23:50:19 -05:00
/* now get the information we need related to the tempo map and the
* timeline
*/
const Temporal : : Beats end_beats ( timepos_t ( end_sample ) . beats ( ) ) ;
2021-08-08 21:08:16 -04:00
Temporal : : TempoMap : : SharedPtr tmap ( Temporal : : TempoMap : : use ( ) ) ;
2021-12-02 13:27:18 -05:00
uint32_t max_chans = 0 ;
2021-12-21 15:37:47 -05:00
TriggerPtr nxt ;
2021-12-02 13:27:18 -05:00
pframes_t dest_offset = 0 ;
2021-11-15 14:27:32 -05:00
2021-11-29 23:50:19 -05:00
while ( nframes ) {
2021-11-15 14:27:32 -05:00
2021-11-29 23:50:19 -05:00
/* start can move if we have to switch triggers in mid-process cycle */
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
const Temporal : : Beats start_beats ( timepos_t ( start_sample ) . beats ( ) ) ;
const double bpm = tmap - > quarters_per_minute_at ( timepos_t ( start_beats ) ) ;
2021-09-05 12:40:58 -04:00
2021-12-02 13:27:18 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " nf loop, ss %1 es %2 sb %3 eb %4 bpm %5 \n " , start_sample , end_sample , start_beats , end_beats , bpm ) ) ;
2021-09-05 12:40:58 -04:00
2021-11-29 23:50:19 -05:00
/* see if there's another trigger explicitly queued */
2021-09-05 12:40:58 -04:00
2021-12-21 15:37:47 -05:00
RingBuffer < uint32_t > : : rw_vector rwv ;
2021-11-29 23:50:19 -05:00
explicit_queue . get_read_vector ( & rwv ) ;
2021-10-19 21:08:46 -04:00
2021-11-29 23:50:19 -05:00
if ( rwv . len [ 0 ] > 0 ) {
2021-09-05 12:40:58 -04:00
2021-11-30 09:57:34 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " explicit queue rvec %1 + %2 \n " , rwv . len [ 0 ] , rwv . len [ 1 ] ) ) ;
2021-09-05 12:40:58 -04:00
2021-11-29 23:50:19 -05:00
/* peek at it without dequeing it */
2021-09-05 12:40:58 -04:00
2021-12-21 15:37:47 -05:00
uint32_t n = * ( rwv . buf [ 0 ] ) ;
nxt = trigger ( n ) ;
2021-09-05 12:40:58 -04:00
2021-11-29 23:50:19 -05:00
/* if user triggered same clip, that will have been handled as
* it processed bang requests . Nothing to do here otherwise .
*/
2021-09-05 12:40:58 -04:00
2021-12-06 10:57:06 -05:00
if ( nxt ! = _currently_playing ) {
2021-09-05 12:40:58 -04:00
2021-11-29 23:50:19 -05:00
/* user has triggered a different slot than the currently waiting-to-play or playing slot */
if ( nxt - > legato ( ) ) {
/* We want to start this trigger immediately, without
* waiting for quantization points , and it should start
* playing at the same internal offset as the current
* trigger .
*/
explicit_queue . increment_read_idx ( 1 ) ; /* consume the entry we peeked at */
2021-12-06 10:57:06 -05:00
nxt - > set_legato_offset ( _currently_playing - > current_pos ( ) ) ;
2021-12-21 19:36:39 -05:00
/* starting up next trigger, check for pending */
maybe_swap_pending ( n ) ;
nxt = trigger ( n ) ;
2021-11-29 23:50:19 -05:00
nxt - > jump_start ( ) ;
2021-12-06 10:57:06 -05:00
_currently_playing - > jump_stop ( ) ;
2021-11-29 23:50:19 -05:00
/* and switch */
2021-12-06 10:57:06 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 => %2 switched to in legato mode \n " , _currently_playing - > index ( ) , nxt - > index ( ) ) ) ;
_currently_playing = nxt ;
PropertyChanged ( Properties : : currently_playing ) ;
2021-11-29 23:50:19 -05:00
} else {
2021-12-14 23:55:52 -05:00
/* no legato-switch */
if ( _currently_playing - > state ( ) = = Trigger : : Stopped ) {
2021-10-19 21:08:46 -04:00
2021-12-14 23:55:52 -05:00
explicit_queue . increment_read_idx ( 1 ) ; /* consume the entry we peeked at */
2021-12-21 19:36:39 -05:00
/* starting up next trigger, check for pending */
maybe_swap_pending ( n ) ;
nxt = trigger ( n ) ;
2021-12-14 23:55:52 -05:00
nxt - > startup ( ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 was finished, started %2 \n " , _currently_playing - > index ( ) , nxt - > index ( ) ) ) ;
_currently_playing = nxt ;
PropertyChanged ( Properties : : currently_playing ) ;
} else if ( _currently_playing - > state ( ) ! = Trigger : : WaitingToStop ) {
/* but just begin stoppingthe currently playing slot */
2021-12-06 10:57:06 -05:00
_currently_playing - > begin_stop ( ) ;
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " start stop for %1 before switching to %2 \n " , _currently_playing - > index ( ) , nxt - > index ( ) ) ) ;
2021-12-14 23:55:52 -05:00
2021-11-29 23:50:19 -05:00
}
}
2021-09-05 12:40:58 -04:00
}
}
2021-12-06 10:57:06 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " trigger %1 complete, state now %2 \n " , _currently_playing - > name ( ) , enum_2_string ( _currently_playing - > state ( ) ) ) ) ;
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
/* if we're not in the process of stopping all active triggers,
* but the current one has stopped , decide which ( if any )
* trigger to play next .
*/
2021-12-06 10:57:06 -05:00
if ( _currently_playing - > state ( ) = = Trigger : : Stopped ) {
2021-12-15 13:19:36 -05:00
2021-12-15 13:03:47 -05:00
if ( ! _stop_all & & ! _currently_playing - > explicitly_stopped ( ) ) {
2021-12-15 13:19:36 -05:00
2021-12-06 10:57:06 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 has stopped, need next... \n " , _currently_playing - > name ( ) ) ) ;
2021-12-15 13:19:36 -05:00
if ( _currently_playing - > use_follow ( ) ) {
int n = determine_next_trigger ( _currently_playing - > index ( ) ) ;
2021-12-22 00:35:59 -05:00
std : : cerr < < " dnt = " < < n < < endl ;
2021-12-15 13:19:36 -05:00
if ( n < 0 ) {
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " %1 finished, no next trigger \n " , _currently_playing - > name ( ) ) ) ;
_currently_playing = 0 ;
PropertyChanged ( Properties : : currently_playing ) ;
break ; /* no triggers to come next, break out of nframes loop */
}
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " switching to next trigger %1 \n " , _currently_playing - > name ( ) ) ) ;
_currently_playing = all_triggers [ n ] ;
_currently_playing - > startup ( ) ;
PropertyChanged ( Properties : : currently_playing ) ;
} else {
_currently_playing = 0 ;
PropertyChanged ( Properties : : currently_playing ) ;
DEBUG_TRACE ( DEBUG : : Triggers , " currently playing was stopped, but stop_all was set, leaving nf loop \n " ) ;
/* leave nframes loop */
break ;
2021-11-29 23:50:19 -05:00
}
2021-12-15 13:19:36 -05:00
2021-11-29 23:50:19 -05:00
} else {
2021-12-15 13:19:36 -05:00
2021-12-06 10:57:06 -05:00
_currently_playing = 0 ;
PropertyChanged ( Properties : : currently_playing ) ;
2021-12-04 13:49:41 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , " currently playing was stopped, but stop_all was set, leaving nf loop \n " ) ;
2021-12-02 13:27:18 -05:00
/* leave nframes loop */
2021-11-29 23:50:19 -05:00
break ;
}
}
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
pframes_t frames_covered ;
2021-08-08 21:08:16 -04:00
2021-08-10 22:35:39 -04:00
2021-12-06 10:57:06 -05:00
boost : : shared_ptr < AudioRegion > ar = boost : : dynamic_pointer_cast < AudioRegion > ( _currently_playing - > region ( ) ) ;
2021-12-02 13:27:18 -05:00
if ( ar ) {
max_chans = std : : max ( ar - > n_channels ( ) , max_chans ) ;
2021-08-10 22:35:39 -04:00
}
2021-08-08 21:08:16 -04:00
2021-12-06 10:57:06 -05:00
frames_covered = _currently_playing - > run ( bufs , start_sample , end_sample , start_beats , end_beats , nframes , dest_offset , _pass_thru , bpm ) ;
2021-09-05 01:19:47 -04:00
2021-11-29 23:50:19 -05:00
nframes - = frames_covered ;
start_sample + = frames_covered ;
2021-12-02 13:27:18 -05:00
dest_offset + = frames_covered ;
2021-08-08 21:08:16 -04:00
2021-12-02 13:27:18 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , string_compose ( " trig %1 ran, covered %2 state now %3 nframes now %4 \n " ,
2021-12-06 10:57:06 -05:00
_currently_playing - > name ( ) , frames_covered , enum_2_string ( _currently_playing - > state ( ) ) , nframes ) ) ;
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
}
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
if ( nframes & & ! _pass_thru ) {
2021-09-05 01:19:47 -04:00
2021-11-29 23:50:19 -05:00
/* didn't cover the entire nframes worth of the buffer, and not
* doing pass thru , so silence whatever is left .
*/
2021-09-05 01:19:47 -04:00
2021-12-21 15:37:47 -05:00
for ( uint32_t chn = 0 ; chn < bufs . count ( ) . n_audio ( ) ; + + chn ) {
2021-11-29 23:50:19 -05:00
AudioBuffer & buf ( bufs . get_audio ( chn ) ) ;
buf . silence ( nframes , ( orig_nframes - nframes ) ) ;
2021-08-10 22:35:39 -04:00
}
}
2021-08-08 21:08:16 -04:00
2021-12-06 10:57:06 -05:00
if ( ! _currently_playing ) {
2021-12-04 13:49:41 -05:00
DEBUG_TRACE ( DEBUG : : Triggers , " nothing currently playing, consider stopping transport \n " ) ;
2021-11-29 23:50:19 -05:00
_stop_all = false ;
if ( active_trigger_boxes . fetch_sub ( 1 ) = = 1 ) {
/* last active trigger box */
_session . stop_transport_from_trigger ( ) ;
}
}
2021-08-08 21:08:16 -04:00
2021-11-29 23:50:19 -05:00
/* audio buffer (channel) count determined by max of input and
2021-12-06 10:57:06 -05:00
* _currently_playing ' s channel count ( if it was audio ) .
2021-11-29 23:50:19 -05:00
*/
2021-09-04 12:37:36 -04:00
2021-11-29 23:50:19 -05:00
ChanCount cc ( DataType : : AUDIO , max_chans ) ;
2021-09-05 12:40:58 -04:00
2021-11-29 23:50:19 -05:00
/* MIDI buffer count not changed */
cc . set_midi ( bufs . count ( ) . n_midi ( ) ) ;
bufs . set_count ( cc ) ;
2021-09-04 12:37:36 -04:00
}
int
2021-12-21 15:37:47 -05:00
TriggerBox : : determine_next_trigger ( uint32_t current )
2021-08-10 22:35:39 -04:00
{
2021-12-21 15:37:47 -05:00
uint32_t n ;
uint32_t runnable = 0 ;
2021-08-10 22:35:39 -04:00
2021-09-04 12:37:36 -04:00
/* count number of triggers that can actually be run (i.e. they have a region) */
2021-12-21 15:37:47 -05:00
for ( uint32_t n = 0 ; n < all_triggers . size ( ) ; + + n ) {
2021-08-10 22:35:39 -04:00
if ( all_triggers [ n ] - > region ( ) ) {
runnable + + ;
}
}
2021-12-22 00:35:59 -05:00
if ( runnable = = 0 | | ! all_triggers [ current ] - > region ( ) ) {
return - 1 ;
}
2021-09-04 12:37:36 -04:00
/* decide which of the two follow actions we're going to use (based on
* random number and the probability setting )
*/
2021-12-21 10:58:22 -05:00
int which_follow_action = 0 ;
2021-12-18 19:38:18 -05:00
2021-08-15 10:04:08 -04:00
int r = _pcg . rand ( 100 ) ; // 0 .. 99
2021-12-18 16:11:54 -05:00
Trigger : : FollowAction fa ;
2021-08-12 01:03:53 -04:00
if ( r < = all_triggers [ current ] - > follow_action_probability ( ) ) {
2021-12-18 16:11:54 -05:00
fa = all_triggers [ current ] - > follow_action ( 0 ) ;
2021-12-21 10:58:22 -05:00
which_follow_action = 0 ;
2021-08-12 01:03:53 -04:00
} else {
2021-12-18 16:11:54 -05:00
fa = all_triggers [ current ] - > follow_action ( 1 ) ;
2021-12-21 10:58:22 -05:00
which_follow_action = 1 ;
2021-08-12 01:03:53 -04:00
}
2021-09-04 12:37:36 -04:00
/* first switch: deal with the "special" cases where we either do
2021-09-05 01:19:47 -04:00
* nothing or just repeat the current trigger
2021-09-04 12:37:36 -04:00
*/
2021-12-18 16:11:54 -05:00
switch ( fa ) {
2021-08-12 01:03:53 -04:00
2021-08-10 22:35:39 -04:00
case Trigger : : Stop :
2021-09-04 12:37:36 -04:00
return - 1 ;
2021-08-12 01:03:53 -04:00
2021-08-10 22:35:39 -04:00
case Trigger : : QueuedTrigger :
/* XXX implement me */
2021-09-04 12:37:36 -04:00
return - 1 ;
2021-08-10 22:35:39 -04:00
default :
if ( runnable = = 1 ) {
2021-09-04 12:37:36 -04:00
/* there's only 1 runnable trigger, so the "next" one
is the same as the current one .
*/
return current ;
2021-08-10 22:35:39 -04:00
}
}
2021-09-04 12:37:36 -04:00
/* second switch: handle the "real" follow actions */
2021-12-18 19:38:18 -05:00
switch ( fa ) {
2021-12-04 15:33:32 -05:00
case Trigger : : None :
return - 1 ;
2021-08-12 00:54:13 -04:00
case Trigger : : Again :
2021-09-04 12:37:36 -04:00
return current ;
2021-08-12 00:54:13 -04:00
2021-08-10 22:35:39 -04:00
case Trigger : : NextTrigger :
n = current ;
while ( true ) {
+ + n ;
if ( n > = all_triggers . size ( ) ) {
2021-11-29 23:50:19 -05:00
cerr < < " loop with n = " < < n < < " of " < < all_triggers . size ( ) < < endl ;
2021-08-10 22:35:39 -04:00
n = 0 ;
2021-08-08 21:08:16 -04:00
}
2021-08-10 22:35:39 -04:00
if ( n = = current ) {
2021-09-05 12:40:58 -04:00
cerr < < " outa here \n " ;
2021-08-10 22:35:39 -04:00
break ;
}
2021-08-08 21:08:16 -04:00
2021-08-10 22:35:39 -04:00
if ( all_triggers [ n ] - > region ( ) & & ! all_triggers [ n ] - > active ( ) ) {
2021-09-04 12:37:36 -04:00
return n ;
2021-08-10 22:35:39 -04:00
}
}
break ;
case Trigger : : PrevTrigger :
n = current ;
while ( true ) {
if ( n = = 0 ) {
n = all_triggers . size ( ) - 1 ;
} else {
n - = 1 ;
}
2021-08-08 21:08:16 -04:00
2021-08-10 22:35:39 -04:00
if ( n = = current ) {
break ;
}
if ( all_triggers [ n ] - > region ( ) & & ! all_triggers [ n ] - > active ( ) ) {
2021-09-04 12:37:36 -04:00
return n ;
2021-08-10 22:35:39 -04:00
}
2021-08-08 21:08:16 -04:00
}
2021-08-10 22:35:39 -04:00
break ;
2021-08-08 21:08:16 -04:00
2021-08-10 22:35:39 -04:00
case Trigger : : FirstTrigger :
for ( n = 0 ; n < all_triggers . size ( ) ; + + n ) {
if ( all_triggers [ n ] - > region ( ) & & ! all_triggers [ n ] - > active ( ) ) {
2021-09-04 12:37:36 -04:00
return n ;
2021-08-10 22:35:39 -04:00
}
2021-08-08 21:08:16 -04:00
}
2021-08-10 22:35:39 -04:00
break ;
case Trigger : : LastTrigger :
2021-08-11 20:14:37 -04:00
for ( int i = all_triggers . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( all_triggers [ i ] - > region ( ) & & ! all_triggers [ i ] - > active ( ) ) {
2021-09-04 12:37:36 -04:00
return i ;
2021-08-10 22:35:39 -04:00
}
}
break ;
case Trigger : : AnyTrigger :
while ( true ) {
2021-08-15 10:04:08 -04:00
n = _pcg . rand ( all_triggers . size ( ) ) ;
2021-08-10 22:35:39 -04:00
if ( ! all_triggers [ n ] - > region ( ) ) {
continue ;
}
if ( all_triggers [ n ] - > active ( ) ) {
continue ;
}
break ;
}
2021-09-04 12:37:36 -04:00
return n ;
2021-08-10 22:35:39 -04:00
case Trigger : : OtherTrigger :
while ( true ) {
2021-08-15 10:04:08 -04:00
n = _pcg . rand ( all_triggers . size ( ) ) ;
2021-12-21 15:37:47 -05:00
if ( ( uint32_t ) n = = current ) {
2021-08-10 22:35:39 -04:00
continue ;
}
if ( ! all_triggers [ n ] - > region ( ) ) {
continue ;
}
if ( all_triggers [ n ] - > active ( ) ) {
continue ;
}
break ;
}
2021-09-04 12:37:36 -04:00
return n ;
2021-08-10 22:35:39 -04:00
/* NOTREACHED */
case Trigger : : Stop :
case Trigger : : QueuedTrigger :
break ;
2021-08-08 21:08:16 -04:00
}
2021-09-04 12:37:36 -04:00
return current ;
2021-08-08 21:08:16 -04:00
}
XMLNode &
TriggerBox : : get_state ( void )
{
2021-08-31 18:46:19 -04:00
XMLNode & node ( Processor : : get_state ( ) ) ;
2021-08-31 21:03:32 -04:00
node . set_property ( X_ ( " type " ) , X_ ( " triggerbox " ) ) ;
2021-08-31 18:46:19 -04:00
node . set_property ( X_ ( " data-type " ) , _data_type . to_string ( ) ) ;
2021-10-13 11:07:07 -04:00
node . set_property ( X_ ( " order " ) , _order ) ;
2021-08-31 18:46:19 -04:00
XMLNode * trigger_child ( new XMLNode ( X_ ( " Triggers " ) ) ) ;
{
Glib : : Threads : : RWLock : : ReaderLock lm ( trigger_lock ) ;
for ( Triggers : : iterator t = all_triggers . begin ( ) ; t ! = all_triggers . end ( ) ; + + t ) {
trigger_child - > add_child_nocopy ( ( * t ) - > get_state ( ) ) ;
}
}
node . add_child_nocopy ( * trigger_child ) ;
2021-10-08 15:37:29 -04:00
if ( _sidechain ) {
XMLNode * scnode = new XMLNode ( X_ ( " Sidechain " ) ) ;
2021-10-10 19:35:15 -04:00
std : : string port_name = _sidechain - > input ( ) - > nth ( 0 ) - > name ( ) ;
port_name = port_name . substr ( 0 , port_name . find ( ' - ' ) ) ;
scnode - > set_property ( X_ ( " name " ) , port_name ) ;
2021-10-08 15:37:29 -04:00
scnode - > add_child_nocopy ( _sidechain - > get_state ( ) ) ;
node . add_child_nocopy ( * scnode ) ;
}
2021-08-31 18:46:19 -04:00
return node ;
2021-08-08 21:08:16 -04:00
}
int
2021-08-31 21:03:32 -04:00
TriggerBox : : set_state ( const XMLNode & node , int version )
2021-08-08 21:08:16 -04:00
{
2021-10-19 14:07:52 -04:00
Processor : : set_state ( node , version ) ;
2021-08-31 21:03:32 -04:00
node . get_property ( X_ ( " data-type " ) , _data_type ) ;
2021-10-13 11:07:07 -04:00
node . get_property ( X_ ( " order " ) , _order ) ;
2021-08-31 21:03:32 -04:00
XMLNode * tnode ( node . child ( X_ ( " Triggers " ) ) ) ;
assert ( tnode ) ;
XMLNodeList const & tchildren ( tnode - > children ( ) ) ;
drop_triggers ( ) ;
{
Glib : : Threads : : RWLock : : WriterLock lm ( trigger_lock ) ;
for ( XMLNodeList : : const_iterator t = tchildren . begin ( ) ; t ! = tchildren . end ( ) ; + + t ) {
2021-12-21 15:37:47 -05:00
TriggerPtr trig ;
2021-08-31 21:03:32 -04:00
if ( _data_type = = DataType : : AUDIO ) {
2021-12-21 15:37:47 -05:00
trig = boost : : make_shared < AudioTrigger > ( all_triggers . size ( ) , * this ) ;
2021-08-31 21:03:32 -04:00
all_triggers . push_back ( trig ) ;
trig - > set_state ( * * t , version ) ;
2021-10-29 18:22:55 -04:00
} else if ( _data_type = = DataType : : MIDI ) {
2021-12-21 15:37:47 -05:00
trig = boost : : make_shared < MIDITrigger > ( all_triggers . size ( ) , * this ) ;
2021-10-29 18:22:55 -04:00
all_triggers . push_back ( trig ) ;
trig - > set_state ( * * t , version ) ;
2021-08-31 21:03:32 -04:00
}
}
}
2021-10-08 15:37:29 -04:00
XMLNode * scnode = node . child ( X_ ( " Sidechain " ) ) ;
if ( scnode ) {
2021-10-10 19:35:15 -04:00
std : : string name ;
scnode - > get_property ( X_ ( " name " ) , name ) ;
add_midi_sidechain ( name ) ;
2021-10-08 15:37:29 -04:00
assert ( _sidechain ) ;
_sidechain - > set_state ( * scnode - > children ( ) . front ( ) , version ) ;
}
2021-08-08 21:08:16 -04:00
return 0 ;
}
2021-10-07 10:03:46 -04:00
2021-10-09 18:58:58 -04:00
void
TriggerBox : : parameter_changed ( std : : string const & param )
{
if ( param = = X_ ( " default-trigger-input-port " ) ) {
reconnect_to_default ( ) ;
}
}
void
TriggerBox : : reconnect_to_default ( )
{
if ( ! _sidechain ) {
return ;
}
_sidechain - > input ( ) - > nth ( 0 ) - > disconnect_all ( ) ;
_sidechain - > input ( ) - > nth ( 0 ) - > connect ( Config - > get_default_trigger_input_port ( ) ) ;
}
2021-11-04 13:16:22 -04:00
MultiAllocSingleReleasePool * TriggerBox : : Request : : pool ;
void
TriggerBox : : init_pool ( )
{
/* "indirection" is because the Request struct is private, and so
nobody else can call its : : init_pool ( ) static method .
*/
Request : : init_pool ( ) ;
}
void
TriggerBox : : Request : : init_pool ( )
{
pool = new MultiAllocSingleReleasePool ( X_ ( " TriggerBoxRequests " ) , sizeof ( TriggerBox : : Request ) , 1024 ) ;
}
void *
TriggerBox : : Request : : operator new ( size_t )
{
return pool - > alloc ( ) ;
}
void
TriggerBox : : Request : : operator delete ( void * ptr , size_t /*size*/ )
{
return pool - > release ( ptr ) ;
}
void
2021-11-04 16:00:16 -04:00
TriggerBox : : request_reload ( int32_t slot , void * ptr )
2021-11-04 13:16:22 -04:00
{
2021-11-04 16:00:16 -04:00
Request * r = new Request ( Request : : Reload ) ;
2021-11-04 13:16:22 -04:00
r - > slot = slot ;
2021-11-04 16:00:16 -04:00
r - > ptr = ptr ;
2021-11-04 13:16:22 -04:00
requests . write ( & r , 1 ) ;
}
void
2021-11-04 16:00:16 -04:00
TriggerBox : : process_requests ( BufferSet & bufs )
2021-11-04 13:16:22 -04:00
{
Request * r ;
while ( requests . read ( & r , 1 ) = = 1 ) {
2021-11-04 16:00:16 -04:00
process_request ( bufs , r ) ;
2021-11-04 13:16:22 -04:00
}
}
void
2021-11-04 16:00:16 -04:00
TriggerBox : : process_request ( BufferSet & bufs , Request * req )
2021-11-04 13:16:22 -04:00
{
switch ( req - > type ) {
case Request : : Use :
2021-11-04 16:00:16 -04:00
std : : cerr < < " Use for " < < req - > slot < < std : : endl ;
2021-11-04 13:16:22 -04:00
break ;
case Request : : Reload :
2021-11-04 16:00:16 -04:00
std : : cerr < < " Reload for " < < req - > slot < < std : : endl ;
reload ( bufs , req - > slot , req - > ptr ) ;
2021-11-04 13:16:22 -04:00
break ;
}
delete req ; /* back to the pool, RT-safe */
}
2021-11-04 16:00:16 -04:00
void
TriggerBox : : reload ( BufferSet & bufs , int32_t slot , void * ptr )
{
2021-11-09 23:47:15 -05:00
if ( slot > = ( int32_t ) all_triggers . size ( ) ) {
2021-11-04 16:00:16 -04:00
return ;
}
std : : cerr < < " reload slot " < < slot < < std : : endl ;
all_triggers [ slot ] - > reload ( bufs , ptr ) ;
}
2021-12-06 11:06:46 -05:00
double
TriggerBox : : position_as_fraction ( ) const
{
2021-12-21 15:37:47 -05:00
TriggerPtr cp = _currently_playing ;
2021-12-06 11:06:46 -05:00
if ( ! cp ) {
return - 1 ;
}
return cp - > position_as_fraction ( ) ;
}
2021-11-09 23:47:15 -05:00
/* Thread */
2021-11-10 00:36:51 -05:00
MultiAllocSingleReleasePool * TriggerBoxThread : : Request : : pool = 0 ;
2021-11-09 23:47:15 -05:00
TriggerBoxThread : : TriggerBoxThread ( )
2021-11-10 00:36:51 -05:00
: requests ( 1024 )
, _xthread ( true )
2021-11-09 23:47:15 -05:00
{
if ( pthread_create_and_store ( " triggerbox thread " , & thread , _thread_work , this ) ) {
error < < _ ( " Session: could not create triggerbox thread " ) < < endmsg ;
throw failed_constructor ( ) ;
}
}
TriggerBoxThread : : ~ TriggerBoxThread ( )
{
void * status ;
2021-11-10 10:45:38 -05:00
char msg = ( char ) Quit ;
_xthread . deliver ( msg ) ;
2021-11-09 23:47:15 -05:00
pthread_join ( thread , & status ) ;
}
void *
TriggerBoxThread : : _thread_work ( void * arg )
{
SessionEvent : : create_per_thread_pool ( " tbthread events " , 4096 ) ;
pthread_set_name ( X_ ( " tbthread " ) ) ;
return ( ( TriggerBoxThread * ) arg ) - > thread_work ( ) ;
}
void *
TriggerBoxThread : : thread_work ( )
{
2021-11-10 00:36:51 -05:00
while ( true ) {
2021-11-09 23:47:15 -05:00
2021-11-10 00:36:51 -05:00
char msg ;
if ( _xthread . receive ( msg , true ) > = 0 ) {
2021-11-10 10:45:38 -05:00
if ( msg = = ( char ) Quit ) {
return ( void * ) 0 ;
abort ( ) ; /*NOTREACHED*/
2021-11-10 00:36:51 -05:00
}
2021-11-10 10:45:38 -05:00
Temporal : : TempoMap : : fetch ( ) ;
Request * req ;
2021-11-09 23:47:15 -05:00
2021-11-10 10:45:38 -05:00
while ( requests . read ( & req , 1 ) = = 1 ) {
switch ( req - > type ) {
case SetRegion :
2021-12-21 17:15:06 -05:00
req - > box - > set_region ( req - > slot , req - > region ) ;
2021-11-10 10:45:38 -05:00
break ;
default :
break ;
}
delete req ; /* back to pool */
}
}
2021-11-09 23:47:15 -05:00
}
2021-11-10 00:36:51 -05:00
return ( void * ) 0 ;
2021-11-09 23:47:15 -05:00
}
void
2021-11-10 00:36:51 -05:00
TriggerBoxThread : : queue_request ( Request * req )
2021-11-09 23:47:15 -05:00
{
2021-11-10 00:36:51 -05:00
char c = req - > type ;
2021-12-18 11:35:10 -05:00
/* Quit is handled by simply delivering the request type (1 byte), with
* no payload in the FIFO . See : : thread_work ( ) above .
*/
2021-11-10 00:36:51 -05:00
if ( req - > type ! = Quit ) {
if ( requests . write ( & req , 1 ) ! = 1 ) {
return ;
}
}
2021-12-18 11:33:51 -05:00
_xthread . deliver ( c ) ;
2021-11-09 23:47:15 -05:00
}
2021-11-10 00:36:51 -05:00
void *
TriggerBoxThread : : Request : : operator new ( size_t )
2021-11-09 23:47:15 -05:00
{
2021-11-10 00:36:51 -05:00
return pool - > alloc ( ) ;
2021-11-09 23:47:15 -05:00
}
void
2021-11-10 00:36:51 -05:00
TriggerBoxThread : : Request : : operator delete ( void * ptr , size_t )
2021-11-09 23:47:15 -05:00
{
2021-11-10 00:36:51 -05:00
pool - > release ( ptr ) ;
2021-11-09 23:47:15 -05:00
}
void
2021-11-10 00:36:51 -05:00
TriggerBoxThread : : Request : : init_pool ( )
2021-11-09 23:47:15 -05:00
{
2021-11-10 00:36:51 -05:00
pool = new MultiAllocSingleReleasePool ( X_ ( " TriggerBoxThreadRequests " ) , sizeof ( TriggerBoxThread : : Request ) , 1024 ) ;
2021-11-09 23:47:15 -05:00
}
2021-11-10 10:45:38 -05:00
void
2021-12-21 15:37:47 -05:00
TriggerBoxThread : : set_region ( TriggerBox & box , uint32_t slot , boost : : shared_ptr < Region > r )
2021-11-10 10:45:38 -05:00
{
TriggerBoxThread : : Request * req = new TriggerBoxThread : : Request ( TriggerBoxThread : : SetRegion ) ;
2021-12-21 15:37:47 -05:00
req - > box = & box ;
req - > slot = slot ;
2021-11-10 10:45:38 -05:00
req - > region = r ;
queue_request ( req ) ;
}