Skip to content

Commit

Permalink
add support for runtime sample rate update
Browse files Browse the repository at this point in the history
  • Loading branch information
gewang committed Nov 21, 2024
1 parent a905014 commit cc2aef7
Show file tree
Hide file tree
Showing 11 changed files with 279 additions and 36 deletions.
12 changes: 12 additions & 0 deletions src/core/chuck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,18 @@ t_CKBOOL ChucK::matchParam( const std::string & lhs, const std::string & rhs )
void ChucK::enactParam( const std::string & name, t_CKINT value )
{
// check and set
if( matchParam(name,CHUCK_PARAM_SAMPLE_RATE) )
{
// check
if( value <= 0 )
{
EM_error2( 0, "(warning) attempt to set CHUCK_PARAM_SAMPLE_RATE to invalid value '%d'; sample rate unchanged...", value );
return;
}
// update VM to new sample rate
// (NOTE: this could be pre-initialization, so need to check VM pointer)
if( vm() ) vm()->update_srate( value );
}
if( matchParam(name,CHUCK_PARAM_TTY_COLOR) )
{
// set the global override switch
Expand Down
11 changes: 11 additions & 0 deletions src/core/chuck_dl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,16 @@ void CK_DLL_CALL ck_register_callback_on_shutdown( Chuck_DL_Query * query, f_cal
query->vm()->register_callback_on_shutdown( cb, bindle );
}

//-----------------------------------------------------------------------------
// name: ck_register_callback_on_srate_update() | 1.5.4.2 (ge) added
// desc: register a callback function to be called on sample rate change
//-----------------------------------------------------------------------------
void CK_DLL_CALL ck_register_callback_on_srate_update( Chuck_DL_Query * query, f_callback_on_srate_update cb, void * bindle )
{
// register
query->vm()->register_callback_on_srate_update( cb, bindle );
}

//-----------------------------------------------------------------------------
// name: ck_register_shreds_watcher()
// desc: register a callback function to receive notifications
Expand Down Expand Up @@ -1435,6 +1445,7 @@ Chuck_DL_Query::Chuck_DL_Query( Chuck_Carrier * carrier, Chuck_DLL * dll )
register_shreds_watcher = ck_register_shreds_watcher; // 1.5.1.5 (ge & andrew)
unregister_shreds_watcher = ck_unregister_shreds_watcher; // 1.5.1.5 (ge & andrew)
register_callback_on_shutdown = ck_register_callback_on_shutdown; // 1.5.2.5 (ge)
register_callback_on_srate_update = ck_register_callback_on_srate_update; // 1.5.4.2 (ge)
m_carrier = carrier;
dll_ref = dll; // 1.5.1.3 (ge) added

Expand Down
14 changes: 8 additions & 6 deletions src/core/chuck_dl.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,12 @@ typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadhook)( void * bindle );
typedef t_CKBOOL (CK_DLL_CALL * f_mainthreadquit)( void * bindle );
// callback function, called on host shutdown
typedef void (CK_DLL_CALL * f_callback_on_shutdown)( void * bindle );
// sample rate update callback | 1.5.4.2 (ge) added
typedef void (CK_DLL_CALL * f_callback_on_srate_update)( t_CKUINT srate, void * bindle );
// shreds watcher callback
typedef void (CK_DLL_CALL * f_shreds_watcher)( Chuck_VM_Shred * SHRED, t_CKINT CODE, t_CKINT PARAM, Chuck_VM * VM, void * BINDLE );
// type instantiation callback
typedef void (CK_DLL_CALL * f_callback_on_instantiate)( Chuck_Object * OBJECT, Chuck_Type * TYPE, Chuck_VM_Shred * originShred, Chuck_VM * VM );
// sample rate update callback | 1.5.4.2 (ge) added
typedef void (* ck_f_srate_cb)( t_CKUINT srate, void * userData );
}


Expand Down Expand Up @@ -426,6 +426,8 @@ typedef t_CKBOOL (CK_DLL_CALL * f_end_class)( Chuck_DL_Query * query );
typedef Chuck_DL_MainThreadHook * (CK_DLL_CALL * f_create_main_thread_hook)( Chuck_DL_Query * query, f_mainthreadhook hook, f_mainthreadquit quit, void * bindle );
// register a callback to be called on host shutdown, e.g., for chugin cleanup
typedef void (CK_DLL_CALL * f_register_callback_on_shutdown)( Chuck_DL_Query * query, f_callback_on_shutdown cb, void * bindle );
// register a callback to be called on sample rate change
typedef void (CK_DLL_CALL * f_register_callback_on_srate_update)( Chuck_DL_Query * query, f_callback_on_srate_update cb, void * bindle );
// register a callback function to receive notification from the VM about shreds (add, remove, etc.)
typedef void (CK_DLL_CALL * f_register_shreds_watcher)( Chuck_DL_Query * query, f_shreds_watcher cb, t_CKUINT options, void * bindle );
// unregister a shreds notification callback
Expand Down Expand Up @@ -622,11 +624,11 @@ struct Chuck_DL_Query

public:
// -------------
// register callback to be invoked by chuck host when sample rate changes
// | 1.5.4.2 (ge) added
// register callback to be invoked by chuck host on
// sample rate changes | 1.5.4.2 (ge) added
// -------------
// register sample rate notification
// f_register_srate_change register_srate_change;
f_register_callback_on_srate_update register_callback_on_srate_update;




Expand Down
119 changes: 96 additions & 23 deletions src/core/chuck_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ t_CKBOOL type_engine_check_class_def( Chuck_Env * env, a_Class_Def class_def );

// helpers
void type_engine_init_op_overload_builtin( Chuck_Env * env );
// update builtin durs according to sample rate
t_CKBOOL type_engine_update_builtin_durs( Chuck_Env * env, t_CKUINT srate );
// check for const
Chuck_Value * type_engine_check_const( Chuck_Env * env, a_Exp exp );
// convert dot member expression to string for printing
Expand Down Expand Up @@ -488,6 +490,86 @@ t_CKBOOL type_engine_init_special( Chuck_Env * env, Chuck_Type * objT )



//-----------------------------------------------------------------------------
// name: type_engine_update_builtin_dur()
// desc: update a specific base duration value
//-----------------------------------------------------------------------------
t_CKBOOL type_engine_update_builtin_dur( Chuck_Env * env,
const string & name, t_CKDUR value )
{
// lookup the value
Chuck_Value * v = env->global()->value.lookup( name );
// check if we found a value
if( !v )
{
EM_error2( 0, "(internal error) cannot find base dur value '%s' in update_builtin_dur()", name.c_str() );
return FALSE;
}
// check if the value is a dur
if( !equals( v->type, env->ckt_dur ) )
{
EM_error2( 0, "(internal error) type mismatch for '%s' in update_builtin_dur()", name.c_str() );
return FALSE;
}
// cast out the dur
t_CKDUR * pDur = (t_CKDUR *)v->addr;
// check the addr
if( pDur == NULL )
{
EM_error2( 0, "(internal error) NULL address for '%s' in update_builtin_dur()", name.c_str() );
return FALSE;
}
// update it to the new value
*pDur = value;
// done
return TRUE;
}




//-----------------------------------------------------------------------------
// callback from VM
//-----------------------------------------------------------------------------
void type_engine_on_srate_update_cb( t_CKUINT srate, void * userdata )
{ type_engine_update_builtin_durs( (Chuck_Env *)userdata, srate ); }
//-----------------------------------------------------------------------------
// name: type_engine_update_builtin_durs()
// desc: update built-in durations, given a sample rate
// NOTE: this should be called whenever the system sample rate changes
//-----------------------------------------------------------------------------
t_CKBOOL type_engine_update_builtin_durs( Chuck_Env * env, t_CKUINT srate )
{
// dur value
t_CKDUR samp = 1.0;
// TODO:
t_CKDUR second = srate * samp;
// t_CKDUR second = 44100 * samp;
t_CKDUR ms = second / 1000.0;
t_CKDUR minute = second * 60.0;
t_CKDUR hour = minute * 60.0;
t_CKDUR day = hour * 24.0;
t_CKDUR week = day * 7.0;
// one billion years, a very long time
// length of a sidereal year; https://en.wikipedia.org/wiki/Year
t_CKDUR eon = day * 365.256363004 * 1000000000.0;

// update
if( !type_engine_update_builtin_dur( env, "samp", samp ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "ms", ms ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "second", second ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "minute", minute ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "hour", hour ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "day", day ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "week", week ) ) return FALSE;
if( !type_engine_update_builtin_dur( env, "eon", eon ) ) return FALSE;

// done
return TRUE;
}




//-----------------------------------------------------------------------------
// name: type_engine_init()
Expand Down Expand Up @@ -545,20 +627,6 @@ t_CKBOOL type_engine_init( Chuck_Carrier * carrier )
env->global()->type.add( env->ckt_cherr->base_name, env->ckt_cherr ); env->ckt_cherr->lock();
// env->global()->type.add( env->ckt_thread->base_name, env->ckt_thread ); env->ckt_thread->lock();

// dur value
t_CKDUR samp = 1.0;
// TODO:
t_CKDUR second = carrier->vm->srate() * samp;
// t_CKDUR second = 44100 * samp;
t_CKDUR ms = second / 1000.0;
t_CKDUR minute = second * 60.0;
t_CKDUR hour = minute * 60.0;
t_CKDUR day = hour * 24.0;
t_CKDUR week = day * 7.0;
// one billion years, a very long time
// length of a sidereal year; https://en.wikipedia.org/wiki/Year
t_CKDUR eon = day * 365.256363004 * 1000000000.0;

// add internal classes
EM_log( CK_LOG_HERALD, "adding base classes..." );
EM_pushlog();
Expand Down Expand Up @@ -616,19 +684,19 @@ t_CKBOOL type_engine_init( Chuck_Carrier * carrier )
// pop indent
EM_poplog();

// default global values
// default global values (except for the durs, which will be updated shortly hereafter)
env->global()->value.add( "null", new Chuck_Value( env->ckt_null, "null", new void *(NULL), TRUE ) );
env->global()->value.add( "NULL", new Chuck_Value( env->ckt_null, "NULL", new void *(NULL), TRUE ) );
env->global()->value.add( "t_zero", new Chuck_Value( env->ckt_time, "time_zero", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "d_zero", new Chuck_Value( env->ckt_dur, "dur_zero", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "samp", new Chuck_Value( env->ckt_dur, "samp", new t_CKDUR(samp), TRUE ) );
env->global()->value.add( "ms", new Chuck_Value( env->ckt_dur, "ms", new t_CKDUR(ms), TRUE ) );
env->global()->value.add( "second", new Chuck_Value( env->ckt_dur, "second", new t_CKDUR(second), TRUE ) );
env->global()->value.add( "minute", new Chuck_Value( env->ckt_dur, "minute", new t_CKDUR(minute), TRUE ) );
env->global()->value.add( "hour", new Chuck_Value( env->ckt_dur, "hour", new t_CKDUR(hour), TRUE ) );
env->global()->value.add( "day", new Chuck_Value( env->ckt_dur, "day", new t_CKDUR(day), TRUE ) );
env->global()->value.add( "week", new Chuck_Value( env->ckt_dur, "week", new t_CKDUR(week), TRUE ) );
env->global()->value.add( "eon", new Chuck_Value( env->ckt_dur, "eon", new t_CKDUR(eon), TRUE ) );
env->global()->value.add( "samp", new Chuck_Value( env->ckt_dur, "samp", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "ms", new Chuck_Value( env->ckt_dur, "ms", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "second", new Chuck_Value( env->ckt_dur, "second", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "minute", new Chuck_Value( env->ckt_dur, "minute", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "hour", new Chuck_Value( env->ckt_dur, "hour", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "day", new Chuck_Value( env->ckt_dur, "day", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "week", new Chuck_Value( env->ckt_dur, "week", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "eon", new Chuck_Value( env->ckt_dur, "eon", new t_CKDUR(0.0), TRUE ) );
env->global()->value.add( "true", new Chuck_Value( env->ckt_int, "true", new t_CKINT(1), TRUE ) );
env->global()->value.add( "false", new Chuck_Value( env->ckt_int, "false", new t_CKINT(0), TRUE ) );
env->global()->value.add( "maybe", new Chuck_Value( env->ckt_int, "maybe", new t_CKFLOAT(.5), FALSE ) );
Expand All @@ -637,6 +705,11 @@ t_CKBOOL type_engine_init( Chuck_Carrier * carrier )
env->global()->value.add( "chout", new Chuck_Value( env->ckt_io, "chout", new Chuck_IO_Chout( carrier ), TRUE ) );
env->global()->value.add( "cherr", new Chuck_Value( env->ckt_io, "cherr", new Chuck_IO_Cherr( carrier ), TRUE ) );

// update the base dur values, according to initial sample rate | 1.5.4.2 (ge) re-factored to support run-time sample rate updates
type_engine_update_builtin_durs( env, carrier->vm->srate() );
// register a callback to be notified when sample rate changes | 1.5.4.2 (ge) added
carrier->vm->register_callback_on_srate_update( type_engine_on_srate_update_cb, env );

// TODO: can't use the following now is local to shred
// env->global()->value.add( "now", new Chuck_Value( env->ckt_time, "now", &(vm->shreduler()->now_system), TRUE ) );

Expand Down
68 changes: 67 additions & 1 deletion src/core/chuck_vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,28 @@ t_CKBOOL Chuck_VM::initialize_synthesis()



//-----------------------------------------------------------------------------
// name: update_srate() | 1.5.4.2 (ge) added
// update sample rate (this will also trigger notifications for srate update)
//-----------------------------------------------------------------------------
t_CKBOOL Chuck_VM::update_srate( t_CKUINT srate )
{
// log
EM_log( CK_LOG_SYSTEM, "updating sample rate: '%d'...", srate );
// set it
m_srate = srate;
// notify registered callbacks of the srate update
notify_callbacks_on_srate_update( srate );
// done
return TRUE;
}




//-----------------------------------------------------------------------------
// name: shutdown()
// desc: ...
// desc: shut it down, VM-wise
//-----------------------------------------------------------------------------
t_CKBOOL Chuck_VM::shutdown()
{
Expand Down Expand Up @@ -1458,6 +1477,29 @@ void Chuck_VM::register_callback_on_shutdown( f_callback_on_shutdown cb, void *



//-----------------------------------------------------------------------------
// name: register_callback_on_srate_update() | 1.5.4.2 (ge) added
// cesc: register a callback to be called on system sample rate update
//-----------------------------------------------------------------------------
void Chuck_VM::register_callback_on_srate_update( f_callback_on_srate_update cb, void * bindle )
{
// check
if( !cb ) return;

// ensure no duplicates
list<Chuck_VM_Callback_On_SampleRate_Update>::iterator it = std::find( m_callbacks_on_srate_update.begin(),
m_callbacks_on_srate_update.end(), cb );
// add if not already preset
if( it == m_callbacks_on_srate_update.end() )
{
// append
m_callbacks_on_srate_update.push_back( Chuck_VM_Callback_On_SampleRate_Update(cb, bindle) );
}
}




//-----------------------------------------------------------------------------
// name: notify_callbacks_on_shutdown()
// desc: notify callbacks on VM shutdown | 1.5.2.5 (ge) added
Expand All @@ -1481,6 +1523,30 @@ void Chuck_VM::notify_callbacks_on_shutdown()



//-----------------------------------------------------------------------------
// name: notify_callbacks_on_srate_update()
// desc: notify callbacks on sample rate update | 1.5.4.2 (ge) added
//-----------------------------------------------------------------------------
void Chuck_VM::notify_callbacks_on_srate_update( t_CKUINT srate )
{
// the function to call eventually
f_callback_on_srate_update f = NULL;
// iterator
list<Chuck_VM_Callback_On_SampleRate_Update>::iterator it;
// iterate
for( it = m_callbacks_on_srate_update.begin();
it != m_callbacks_on_srate_update.end(); it++ )
{
// the function
f = (*it).cb;
// call it with the srate and user data
f( srate, (*it).userdata );
}
}




//-----------------------------------------------------------------------------
// name: notify_watchers()
// desc: notify watchers for a particular subscription | 1.5.1.5
Expand Down
Loading

0 comments on commit cc2aef7

Please sign in to comment.