2021-08-15 20:07:12 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
# include "cwMem.h"
# include "cwText.h"
# include "cwObject.h"
# include "cwVectOps.h"
# include "cwMtx.h"
2021-08-23 02:41:33 +00:00
# include "cwDspTypes.h" // real_t, sample_t
2024-01-13 15:18:21 +00:00
# include "cwFlowDecl.h"
2021-12-11 20:17:11 +00:00
# include "cwFlow.h"
2021-08-15 20:07:12 +00:00
# include "cwFlowTypes.h"
namespace cw
{
namespace flow
{
idLabelPair_t typeLabelFlagsA [ ] = {
{ kBoolTFl , " bool " } ,
{ kUIntTFl , " uint " } ,
{ kIntTFl , " int " , } ,
2021-08-23 02:41:33 +00:00
{ kFloatTFl , " float " } ,
{ kRealTFl , " real " } ,
{ kDoubleTFl , " double " } ,
2021-08-15 20:07:12 +00:00
{ kBoolMtxTFl , " bool_mtx " } ,
{ kUIntMtxTFl , " uint_mtx " } ,
{ kIntMtxTFl , " int_mtx " } ,
2021-08-23 02:41:33 +00:00
{ kFloatMtxTFl , " float_mtx " } ,
{ kDoubleMtxTFl , " double_mtx " } ,
2021-08-15 20:07:12 +00:00
{ kABufTFl , " audio " } ,
{ kFBufTFl , " spectrum " } ,
{ kStringTFl , " string " } ,
{ kTimeTFl , " time " } ,
{ kInvalidTFl , nullptr }
} ;
const char * _typeFlagToLabel ( unsigned flag )
{
for ( unsigned i = 0 ; typeLabelFlagsA [ i ] . id ! = kInvalidTFl ; + + i )
if ( typeLabelFlagsA [ i ] . id = = flag )
return typeLabelFlagsA [ i ] . label ;
return " <unknown-type> " ;
}
void _value_release ( value_t * v )
{
if ( v = = nullptr )
return ;
switch ( v - > flags & kTypeMask )
{
case kInvalidTFl :
break ;
case kBoolTFl :
case kUIntTFl :
case kIntTFl :
2021-08-23 02:41:33 +00:00
case kFloatTFl :
case kDoubleTFl :
2021-08-15 20:07:12 +00:00
break ;
case kABufTFl :
abuf_destroy ( v - > u . abuf ) ;
break ;
case kFBufTFl :
fbuf_destroy ( v - > u . fbuf ) ;
break ;
case kBoolMtxTFl :
case kUIntMtxTFl :
case kIntMtxTFl :
2021-08-23 02:41:33 +00:00
case kFloatMtxTFl :
case kDoubleMtxTFl :
2021-08-15 20:07:12 +00:00
assert ( 0 ) ; // not implemeneted
break ;
case kStringTFl :
mem : : release ( v - > u . s ) ;
break ;
2021-08-23 02:41:33 +00:00
case kTimeTFl :
assert ( 0 ) ;
break ;
default :
assert ( 0 ) ;
break ;
}
v - > flags = kInvalidTFl ;
}
void _value_duplicate ( value_t & dst , const value_t & src )
{
switch ( src . flags & kTypeMask )
{
case kInvalidTFl :
break ;
case kBoolTFl :
case kUIntTFl :
case kIntTFl :
case kFloatTFl :
case kDoubleTFl :
dst = src ;
break ;
case kABufTFl :
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
dst . u . abuf = src . u . abuf = = nullptr ? nullptr : abuf_duplicate ( src . u . abuf ) ;
dst . flags = src . flags ;
break ;
case kFBufTFl :
dst . u . fbuf = src . u . fbuf = = nullptr ? nullptr : fbuf_duplicate ( src . u . fbuf ) ;
dst . flags = src . flags ;
break ;
case kBoolMtxTFl :
case kUIntMtxTFl :
case kIntMtxTFl :
case kFloatMtxTFl :
case kDoubleMtxTFl :
assert ( 0 ) ; // not implemeneted
2021-08-15 20:07:12 +00:00
break ;
2021-08-23 02:41:33 +00:00
case kStringTFl :
dst . u . s = mem : : duplStr ( dst . u . s ) ;
dst . flags = src . flags ;
break ;
2021-08-15 20:07:12 +00:00
case kTimeTFl :
assert ( 0 ) ;
break ;
default :
assert ( 0 ) ;
break ;
}
}
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
void _value_print ( const value_t * v )
{
if ( v = = nullptr )
return ;
switch ( v - > flags & kTypeMask )
{
case kInvalidTFl :
break ;
case kBoolTFl : printf ( " %s " , v - > u . b ? " True " : " False " ) ; break ;
case kUIntTFl : printf ( " %i " , v - > u . u ) ; break ;
case kIntTFl : printf ( " %i " , v - > u . i ) ; break ;
2021-08-23 02:41:33 +00:00
case kFloatTFl : printf ( " %f " , v - > u . f ) ; break ;
case kDoubleTFl : printf ( " %f " , v - > u . d ) ; break ;
case kABufTFl :
if ( v - > u . abuf = = nullptr )
printf ( " abuf: <null> " ) ;
else
printf ( " abuf: chN:%i frameN:%i srate:%8.1f " , v - > u . abuf - > chN , v - > u . abuf - > frameN , v - > u . abuf - > srate ) ;
break ;
case kFBufTFl :
if ( v - > u . fbuf = = nullptr )
printf ( " fbuf: <null> " ) ;
else
2022-11-11 18:51:06 +00:00
{
printf ( " fbuf: chN:%i srate:%8.1f " , v - > u . fbuf - > chN , v - > u . fbuf - > srate ) ;
2022-12-22 20:25:54 +00:00
for ( unsigned i = 0 ; i < v - > u . fbuf - > chN ; + + i )
2022-11-11 18:51:06 +00:00
printf ( " (binN:%i hopSmpN:%i) " , v - > u . fbuf - > binN_V [ i ] , v - > u . fbuf - > hopSmpN_V [ i ] ) ;
}
2021-08-23 02:41:33 +00:00
break ;
2021-08-15 20:07:12 +00:00
case kBoolMtxTFl :
case kUIntMtxTFl :
case kIntMtxTFl :
2021-08-23 02:41:33 +00:00
case kFloatMtxTFl :
case kDoubleMtxTFl :
2021-08-15 20:07:12 +00:00
assert ( 0 ) ; // not implemeneted
break ;
2021-08-23 02:41:33 +00:00
case kStringTFl :
printf ( " %s " , v - > u . s ) ;
break ;
2021-08-15 20:07:12 +00:00
case kTimeTFl :
assert ( 0 ) ;
break ;
default :
assert ( 0 ) ;
break ;
}
}
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
rc_t _val_get ( const value_t * val , bool & valRef )
{
rc_t rc = kOkRC ;
switch ( val - > flags & kTypeMask )
{
case kBoolTFl : valRef = val - > u . b ; break ;
case kUIntTFl : valRef = val - > u . u ! = 0 ; break ;
case kIntTFl : valRef = val - > u . i ! = 0 ; break ;
case kFloatTFl : valRef = val - > u . f ! = 0 ; break ;
case kDoubleTFl : valRef = val - > u . d ! = 0 ; break ;
default :
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to a bool. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
}
return rc ;
}
rc_t _val_get ( const value_t * val , uint_t & valRef )
{
rc_t rc = kOkRC ;
switch ( val - > flags & kTypeMask )
{
case kBoolTFl : valRef = val - > u . b ? 1 : 0 ; break ;
case kUIntTFl : valRef = val - > u . u ; break ;
case kIntTFl : valRef = val - > u . i ; break ;
case kFloatTFl : valRef = ( uint_t ) val - > u . f ; break ;
case kDoubleTFl : valRef = ( uint_t ) val - > u . d ; break ;
default :
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to a uint_t. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
}
return rc ;
}
rc_t _val_get ( const value_t * val , int_t & valRef )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc = kOkRC ;
switch ( val - > flags & kTypeMask )
{
case kBoolTFl : valRef = val - > u . b ? 1 : 0 ; break ;
case kUIntTFl : valRef = ( int_t ) val - > u . u ; break ;
case kIntTFl : valRef = val - > u . i ; break ;
case kFloatTFl : valRef = ( int_t ) val - > u . f ; break ;
case kDoubleTFl : valRef = ( int_t ) val - > u . d ; break ;
default :
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to a int_t. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
}
return rc ;
}
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
rc_t _val_get ( const value_t * val , float & valRef )
{
rc_t rc = kOkRC ;
switch ( val - > flags & kTypeMask )
{
case kBoolTFl : valRef = val - > u . b ? 1 : 0 ; break ;
case kUIntTFl : valRef = ( float ) val - > u . u ; break ;
case kIntTFl : valRef = ( float ) val - > u . i ; break ;
case kFloatTFl : valRef = ( float ) val - > u . f ; break ;
case kDoubleTFl : valRef = ( float ) val - > u . d ; break ;
default :
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to a float. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
}
return rc ;
}
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
rc_t _val_get ( const value_t * val , double & valRef )
{
rc_t rc = kOkRC ;
switch ( val - > flags & kTypeMask )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
case kBoolTFl : valRef = val - > u . b ? 1 : 0 ; break ;
case kUIntTFl : valRef = ( double ) val - > u . u ; break ;
case kIntTFl : valRef = ( double ) val - > u . i ; break ;
case kFloatTFl : valRef = ( double ) val - > u . f ; break ;
case kDoubleTFl : valRef = val - > u . d ; break ;
default :
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to a double. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
}
return rc ;
}
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
rc_t _val_get ( const value_t * val , const char * & valRef )
{
rc_t rc = kOkRC ;
if ( cwIsFlag ( val - > flags & kTypeMask , kStringTFl ) )
valRef = val - > u . s ;
else
{
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to a string. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
valRef = nullptr ;
2021-08-15 20:07:12 +00:00
}
return rc ;
}
2021-08-23 02:41:33 +00:00
rc_t _val_get ( value_t * val , abuf_t * & valRef )
{
rc_t rc = kOkRC ;
if ( cwIsFlag ( val - > flags & kTypeMask , kABufTFl ) )
valRef = val - > u . abuf ;
else
{
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to an abuf_t. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
valRef = nullptr ;
}
return rc ;
}
rc_t _val_get ( value_t * val , const abuf_t * & valRef )
{
abuf_t * non_const_val ;
rc_t rc = kOkRC ;
if ( ( rc = _val_get ( val , non_const_val ) ) = = kOkRC )
valRef = non_const_val ;
return rc ;
}
rc_t _val_get ( value_t * val , fbuf_t * & valRef )
{
rc_t rc = kOkRC ;
if ( cwIsFlag ( val - > flags & kTypeMask , kFBufTFl ) )
valRef = val - > u . fbuf ;
else
{
valRef = nullptr ;
2022-12-27 23:09:31 +00:00
rc = cwLogError ( kTypeMismatchRC , " The type 0x%x could not be converted to an fbuf_t. " , val - > flags ) ;
2021-08-23 02:41:33 +00:00
}
return rc ;
}
rc_t _val_get ( value_t * val , const fbuf_t * & valRef )
{
fbuf_t * non_const_val ;
rc_t rc = kOkRC ;
if ( ( rc = _val_get ( val , non_const_val ) ) = = kOkRC )
valRef = non_const_val ;
return rc ;
}
2022-01-22 14:49:45 +00:00
template < typename T >
rc_t _val_get_driver ( const variable_t * var , T & valRef )
{
if ( var = = nullptr )
return cwLogError ( kInvalidArgRC , " Cannnot get the value of a non-existent variable. " ) ;
if ( var - > value = = nullptr )
return cwLogError ( kInvalidStateRC , " No value has been assigned to the variable: %s.%s ch:%i. " , cwStringNullGuard ( var - > inst - > label ) , cwStringNullGuard ( var - > label ) , var - > chIdx ) ;
return _val_get ( var - > value , valRef ) ;
}
2021-08-23 02:41:33 +00:00
rc_t _var_find_to_set ( instance_t * inst , unsigned vid , unsigned chIdx , unsigned typeFl , variable_t * & varRef )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc = kOkRC ;
varRef = nullptr ;
//
if ( ( rc = var_find ( inst , vid , chIdx , varRef ) ) = = kOkRC )
{
// validate the type of the variable against the description
if ( ! cwIsFlag ( varRef - > varDesc - > type , typeFl ) )
rc = cwLogError ( kTypeMismatchRC , " Type mismatch. Instance:%s variable:%s with type 0x%x does not match requested type:0x%x. " , varRef - > inst - > label , varRef - > label , varRef - > varDesc - > type , typeFl ) ;
}
return rc ;
}
// Variable lookup: Exact match on vid and chIdx
rc_t _var_find_on_vid_and_ch ( instance_t * inst , unsigned vid , unsigned chIdx , variable_t * & varRef )
{
varRef = nullptr ;
for ( variable_t * var = inst - > varL ; var ! = nullptr ; var = var - > var_link )
{
// the variable vid and chIdx should form a unique pair
if ( var - > vid = = vid & & var - > chIdx = = chIdx )
{
varRef = var ;
return kOkRC ;
}
}
return cwLogError ( kInvalidIdRC , " The variable matching id:%i ch:%i on instance '%s' could not be found. " , vid , chIdx , inst - > label ) ;
}
// Variable lookup: Exact match on label and chIdx
variable_t * _var_find_on_label_and_ch ( instance_t * inst , const char * var_label , unsigned chIdx )
{
for ( variable_t * var = inst - > varL ; var ! = nullptr ; var = var - > var_link )
{
// the variable vid and chIdx should form a unique pair
if ( textCompare ( var - > label , var_label ) = = 0 & & var - > chIdx = = chIdx )
2021-08-15 20:07:12 +00:00
return var ;
2021-08-23 02:41:33 +00:00
}
2021-08-15 20:07:12 +00:00
return nullptr ;
2021-08-23 02:41:33 +00:00
}
2021-08-15 20:07:12 +00:00
rc_t _validate_var_assignment ( variable_t * var , unsigned typeFl )
{
if ( cwIsFlag ( var - > varDesc - > flags , kSrcVarFl ) )
return cwLogError ( kInvalidStateRC , " The variable '%s' on instance '%s' cannot be set because it is a 'src' variable. " , var - > label , var - > inst - > label ) ;
if ( ! cwIsFlag ( var - > varDesc - > type , typeFl ) )
return cwLogError ( kTypeMismatchRC , " The variable '%s' on instance '%s' is not a '%s'. " , var - > label , var - > inst - > label , _typeFlagToLabel ( typeFl ) ) ;
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
return kOkRC ;
}
rc_t _var_broadcast_new_value ( variable_t * var )
{
rc_t rc = kOkRC ;
2021-12-30 02:52:46 +00:00
/*
2021-08-15 20:07:12 +00:00
// notify each connected var that the value has changed
for ( variable_t * con_var = var - > connect_link ; con_var ! = nullptr ; con_var = con_var - > connect_link )
if ( ( rc = con_var - > inst - > class_desc - > members - > value ( con_var - > inst , con_var ) ) ! = kOkRC )
break ;
2021-12-30 02:52:46 +00:00
*/
return rc ;
}
template < typename T >
2022-01-22 14:49:45 +00:00
void _var_setter ( variable_t * var , unsigned local_value_idx , T val )
2021-12-30 02:52:46 +00:00
{
cwLogError ( kAssertFailRC , " Unimplemented variable setter. " ) ;
assert ( 0 ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < bool > ( variable_t * var , unsigned local_value_idx , bool val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . b = val ;
var - > local_value [ local_value_idx ] . flags = kBoolTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %i (bool). " , var - > inst - > label , var - > label , var - > chIdx , val ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < unsigned > ( variable_t * var , unsigned local_value_idx , unsigned val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . u = val ;
var - > local_value [ local_value_idx ] . flags = kUIntTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %i (uint). " , var - > inst - > label , var - > label , var - > chIdx , val ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < int > ( variable_t * var , unsigned local_value_idx , int val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . i = val ;
var - > local_value [ local_value_idx ] . flags = kIntTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %i (int). " , var - > inst - > label , var - > label , var - > chIdx , val ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < float > ( variable_t * var , unsigned local_value_idx , float val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . f = val ;
var - > local_value [ local_value_idx ] . flags = kFloatTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %f (float). " , var - > inst - > label , var - > label , var - > chIdx , val ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < double > ( variable_t * var , unsigned local_value_idx , double val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . d = val ;
var - > local_value [ local_value_idx ] . flags = kDoubleTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %f (double). " , var - > inst - > label , var - > label , var - > chIdx , val ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < const char * > ( variable_t * var , unsigned local_value_idx , const char * val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . s = mem : : duplStr ( val ) ;
var - > local_value [ local_value_idx ] . flags = kStringTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %s (string). " , var - > inst - > label , var - > label , var - > chIdx , val ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < abuf_t * > ( variable_t * var , unsigned local_value_idx , abuf_t * val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . abuf = val ;
var - > local_value [ local_value_idx ] . flags = kABufTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %s (abuf). " , var - > inst - > label , var - > label , var - > chIdx , abuf = = nullptr ? " null " : " valid " ) ;
}
template < >
2022-01-22 14:49:45 +00:00
void _var_setter < fbuf_t * > ( variable_t * var , unsigned local_value_idx , fbuf_t * val )
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
var - > local_value [ local_value_idx ] . u . fbuf = val ;
var - > local_value [ local_value_idx ] . flags = kFBufTFl ;
2021-12-30 02:52:46 +00:00
cwLogMod ( " %s.%s ch:%i %s (fbuf). " , var - > inst - > label , var - > label , var - > chIdx , fbuf = = nullptr ? " null " : " valid " ) ;
}
template < typename T >
rc_t _var_set_template ( variable_t * var , unsigned typeFlag , T val )
{
rc_t rc ;
2022-01-22 14:49:45 +00:00
unsigned next_local_value_idx = ( var - > local_value_idx + 1 ) % kLocalValueN ;
// store the pointer to the current value of this variable
value_t * original_value = var - > value ;
unsigned original_value_idx = var - > local_value_idx ;
2021-12-30 02:52:46 +00:00
2022-01-22 14:49:45 +00:00
// verify that this is a legal assignment
2021-12-30 02:52:46 +00:00
if ( ( rc = _validate_var_assignment ( var , typeFlag ) ) ! = kOkRC )
2022-01-22 14:49:45 +00:00
{
goto errLabel ;
}
// release the previous value in the next slot
_value_release ( & var - > local_value [ next_local_value_idx ] ) ;
// set the new local value
_var_setter ( var , next_local_value_idx , val ) ;
2021-12-30 02:52:46 +00:00
2022-01-22 14:49:45 +00:00
// make the new local value current
var - > value = var - > local_value + next_local_value_idx ;
var - > local_value_idx = next_local_value_idx ;
2021-12-30 02:52:46 +00:00
// If the instance is fully initialized ...
if ( var - > inst - > varMapA ! = nullptr )
{
// ... then inform the proc. that the value changed
2022-01-22 14:49:45 +00:00
// Note 1: We don't want to this call to occur if we are inside or prior to 'proc.create()'
2021-12-30 02:52:46 +00:00
// call because calls' to 'proc.value()' will see the instance in a incomplete state)
2022-01-22 14:49:45 +00:00
// Note 2: If this call returns an error then the value assignment is cancelled
// and the value does not change.
rc = var - > inst - > class_desc - > members - > value ( var - > inst , var ) ;
2021-12-30 02:52:46 +00:00
}
if ( rc = = kOkRC )
{
// send the value to connected downstream proc's
rc = _var_broadcast_new_value ( var ) ;
}
2022-01-22 14:49:45 +00:00
else
2021-12-30 02:52:46 +00:00
{
2022-01-22 14:49:45 +00:00
// cancel the assignment and restore the original value
var - > value = original_value ;
var - > local_value_idx = original_value_idx ;
2021-12-30 02:52:46 +00:00
}
2022-01-22 14:49:45 +00:00
errLabel :
2021-12-30 02:52:46 +00:00
return rc ;
2021-08-15 20:07:12 +00:00
}
2022-01-22 14:49:45 +00:00
bool is_connected_to_external_proc ( const variable_t * var )
2021-08-15 20:07:12 +00:00
{
2022-01-22 14:49:45 +00:00
// if this var does not have a 'src_ptr' then it can't be connected to an external proc
if ( var - > src_var = = nullptr | | var - > value = = nullptr )
return false ;
2021-08-23 02:41:33 +00:00
2022-01-22 14:49:45 +00:00
// if this var is using a local value then it can't be connected to an external proc
for ( unsigned i = 0 ; i < kLocalValueN ; + + i )
if ( var - > value = = var - > local_value + i )
return false ;
2021-08-15 20:07:12 +00:00
2022-01-22 14:49:45 +00:00
return true ;
2021-12-30 02:52:46 +00:00
}
2021-08-23 02:41:33 +00:00
template < typename T >
2021-12-30 02:52:46 +00:00
rc_t _var_set_driver ( variable_t * var , unsigned typeFlag , T value )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc ;
2021-08-15 20:07:12 +00:00
2021-12-30 02:52:46 +00:00
// if this variable is fed from the output of an external proc - then it's local value cannot be set
if ( is_connected_to_external_proc ( var ) )
return kOkRC ;
2021-08-23 02:41:33 +00:00
// if this assignment targets a specific channel ...
if ( var - > chIdx ! = kAnyChIdx )
{
2021-12-30 02:52:46 +00:00
rc = _var_set_template ( var , typeFlag , value ) ; // ... then set it alone
2021-08-23 02:41:33 +00:00
}
else // ... otherwise set all channels.
{
for ( ; var ! = nullptr ; var = var - > ch_link )
2021-12-30 02:52:46 +00:00
if ( ( rc = _var_set_template ( var , typeFlag , value ) ) ! = kOkRC )
2021-08-23 02:41:33 +00:00
break ;
}
return rc ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
rc_t _var_register_and_set ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , abuf_t * abuf )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc ;
2021-08-15 20:07:12 +00:00
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = var_register_and_set ( inst , var_label , vid , chIdx , var ) ) ! = kOkRC )
return rc ;
if ( var ! = nullptr )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kABufTFl , abuf ) ;
2021-08-23 02:41:33 +00:00
return rc ;
}
rc_t _var_register_and_set ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , fbuf_t * fbuf )
{
rc_t rc ;
variable_t * var = nullptr ;
if ( ( rc = var_register_and_set ( inst , var_label , vid , chIdx , var ) ) ! = kOkRC )
return rc ;
if ( var ! = nullptr )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kFBufTFl , fbuf ) ;
2021-08-23 02:41:33 +00:00
return rc ;
}
rc_t _set_var_value_from_cfg ( variable_t * var , const object_t * value )
{
rc_t rc = kOkRC ;
2021-08-15 20:07:12 +00:00
2021-12-30 02:52:46 +00:00
unsigned typeFlag = var - > varDesc - > type & kTypeMask ;
switch ( typeFlag )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
case kBoolTFl :
{
bool v ;
if ( ( rc = value - > value ( v ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
rc = _var_set_driver ( var , typeFlag , v ) ;
2021-08-23 02:41:33 +00:00
}
break ;
2021-08-15 20:07:12 +00:00
case kUIntTFl :
{
unsigned v ;
if ( ( rc = value - > value ( v ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
rc = _var_set_driver ( var , typeFlag , v ) ;
2021-08-15 20:07:12 +00:00
}
break ;
case kIntTFl :
{
int v ;
if ( ( rc = value - > value ( v ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
rc = _var_set_driver ( var , typeFlag , v ) ;
2021-08-15 20:07:12 +00:00
}
break ;
2021-08-23 02:41:33 +00:00
case kFloatTFl :
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
float v ;
2021-08-15 20:07:12 +00:00
if ( ( rc = value - > value ( v ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
rc = _var_set_driver ( var , typeFlag , v ) ;
2021-08-23 02:41:33 +00:00
}
break ;
case kDoubleTFl :
{
double v ;
if ( ( rc = value - > value ( v ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
rc = _var_set_driver ( var , typeFlag , v ) ;
2021-08-15 20:07:12 +00:00
}
break ;
2021-08-23 02:41:33 +00:00
case kStringTFl :
{
const char * v ;
if ( ( rc = value - > value ( v ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
rc = _var_set_driver ( var , typeFlag , v ) ;
2021-08-23 02:41:33 +00:00
}
break ;
2021-08-15 20:07:12 +00:00
default :
rc = cwLogError ( kOpFailRC , " The variable type 0x%x cannot yet be set via a preset. " , var - > varDesc - > type ) ;
goto errLabel ;
}
errLabel :
if ( rc ! = kOkRC )
2021-08-23 02:41:33 +00:00
rc = cwLogError ( kSyntaxErrorRC , " The %s.%s could not extract a type:%s from a configuration value. " , var - > inst - > label , var - > label , _typeFlagToLabel ( var - > varDesc - > type & kTypeMask ) ) ;
2021-08-15 20:07:12 +00:00
return rc ;
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
rc_t _var_map_id_to_index ( instance_t * inst , unsigned vid , unsigned chIdx , unsigned & idxRef )
{
2021-12-30 02:52:46 +00:00
unsigned idx = vid * inst - > varMapChN + ( chIdx = = kAnyChIdx ? 0 : ( chIdx + 1 ) ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// verify that the map idx is valid
if ( idx > = inst - > varMapN )
2021-12-30 02:52:46 +00:00
return cwLogError ( kAssertFailRC , " The variable map positioning location %i is out of the range %i on instance '%s' vid:%i ch:%i. " , idx , inst - > varMapN , inst - > label , vid , chIdx ) ;
2021-08-23 02:41:33 +00:00
idxRef = idx ;
return kOkRC ;
}
rc_t _var_map_label_to_index ( instance_t * inst , const char * var_label , unsigned chIdx , unsigned & idxRef )
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
idxRef = kInvalidIdx ;
if ( ( rc = var_find ( inst , var_label , chIdx , var ) ) = = kOkRC )
rc = _var_map_id_to_index ( inst , var - > vid , chIdx , idxRef ) ;
return rc ;
}
rc_t _var_add_to_ch_list ( instance_t * inst , variable_t * new_var )
{
rc_t rc = kOkRC ;
variable_t * base_var = nullptr ;
variable_t * v0 = nullptr ;
variable_t * v1 = nullptr ;
if ( new_var - > chIdx = = kAnyChIdx )
return kOkRC ;
if ( ( base_var = _var_find_on_label_and_ch ( inst , new_var - > label , kAnyChIdx ) ) = = nullptr )
{
rc = cwLogError ( kInvalidStateRC , " The base channel variable does not exist for '%s.%s'. This is an illegal state. " , inst - > label , new_var - > label ) ;
goto errLabel ;
}
// insert v0 in order by channel number
for ( v0 = base_var , v1 = base_var - > ch_link ; v1 ! = nullptr ; v1 = v1 - > ch_link )
{
if ( v1 - > chIdx > new_var - > chIdx )
break ;
v0 = v1 ;
}
// the new var channel index should never match the previous or next channel index
assert ( v0 - > chIdx ! = new_var - > chIdx & & ( v1 = = nullptr | | v1 - > chIdx ! = new_var - > chIdx ) ) ;
new_var - > ch_link = v1 ;
v0 - > ch_link = new_var ;
errLabel :
return rc ;
}
// Create a variable and set it's value from 'value_cfg'.
// If 'value_cfg' is null then use the value from var->varDesc->val_cfg.
rc_t _var_create ( instance_t * inst , const char * var_label , unsigned id , unsigned chIdx , const object_t * value_cfg , variable_t * & varRef )
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
var_desc_t * vd = nullptr ;
varRef = nullptr ;
// if this var already exists - it can't be created again
if ( ( var = _var_find_on_label_and_ch ( inst , var_label , chIdx ) ) ! = nullptr )
{
rc = cwLogError ( kInvalidStateRC , " The variable '%s' ch:%i has already been created on the instance: '%s'. " , var_label , chIdx , inst - > label ) ;
goto errLabel ;
}
// locate the var desc
if ( ( vd = var_desc_find ( inst - > class_desc , var_label ) ) = = nullptr )
{
rc = cwLogError ( kInvalidIdRC , " Unable to locate the variable '%s' in class '%s'. " , var_label , inst - > class_desc - > label ) ;
goto errLabel ;
}
// create the var
var = mem : : allocZ < variable_t > ( ) ;
var - > varDesc = vd ;
var - > inst = inst ;
var - > label = mem : : duplStr ( var_label ) ;
var - > vid = id ;
var - > chIdx = chIdx ;
var - > value = nullptr ;
// if no value was given then set the value to the value given in the class
if ( value_cfg = = nullptr )
value_cfg = var - > varDesc - > val_cfg ;
// if value_cfg is valid set the variable value
if ( value_cfg ! = nullptr )
if ( ( rc = _set_var_value_from_cfg ( var , value_cfg ) ) ! = kOkRC )
goto errLabel ;
var - > var_link = inst - > varL ;
inst - > varL = var ;
// link the new var into the ch_link list
if ( ( rc = _var_add_to_ch_list ( inst , var ) ) ! = kOkRC )
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
{
_var_destroy ( var ) ;
cwLogError ( kOpFailRC , " Variable creation failed on '%s.%s' ch:%i. " , inst - > label , var_label , chIdx ) ;
}
else
{
varRef = var ;
cwLogMod ( " Created var: %s.%s ch:%i. " , inst - > label , var_label , chIdx ) ;
}
return rc ;
}
void _var_print ( const variable_t * var )
{
2021-12-30 02:52:46 +00:00
const char * conn_label = is_connected_to_external_proc ( var ) ? " extern " : " " ;
2021-08-23 02:41:33 +00:00
2021-12-30 02:52:46 +00:00
printf ( " %20s id:%4i ch:%3i : %s : " , var - > label , var - > vid , var - > chIdx , conn_label ) ;
2021-08-23 02:41:33 +00:00
if ( var - > value = = nullptr )
2022-01-22 14:49:45 +00:00
_value_print ( & var - > local_value [ 0 ] ) ;
2021-08-23 02:41:33 +00:00
else
_value_print ( var - > value ) ;
printf ( " \n " ) ;
}
rc_t _preset_set_var_value ( instance_t * inst , const char * var_label , unsigned chIdx , const object_t * value )
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
// get the variable
if ( ( rc = var_find ( inst , var_label , chIdx , var ) ) ! = kOkRC )
goto errLabel ;
rc = _set_var_value_from_cfg ( var , value ) ;
errLabel :
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " The value of instance:%s variable:%s could not be set via a preset. " , inst - > label , var_label ) ;
return rc ;
}
}
}
cw : : flow : : abuf_t * cw : : flow : : abuf_create ( srate_t srate , unsigned chN , unsigned frameN )
{
abuf_t * a = mem : : allocZ < abuf_t > ( ) ;
a - > srate = srate ;
a - > chN = chN ;
a - > frameN = frameN ;
2021-08-15 20:07:12 +00:00
a - > buf = mem : : allocZ < sample_t > ( chN * frameN ) ;
return a ;
}
2022-01-22 14:49:45 +00:00
void cw : : flow : : abuf_destroy ( abuf_t * & abuf )
2021-08-15 20:07:12 +00:00
{
if ( abuf = = nullptr )
return ;
mem : : release ( abuf - > buf ) ;
mem : : release ( abuf ) ;
}
2021-08-23 02:41:33 +00:00
cw : : flow : : abuf_t * cw : : flow : : abuf_duplicate ( const abuf_t * src )
{
return abuf_create ( src - > srate , src - > chN , src - > frameN ) ;
}
2021-08-15 20:07:12 +00:00
cw : : rc_t cw : : flow : : abuf_set_channel ( abuf_t * abuf , unsigned chIdx , const sample_t * v , unsigned vN )
{
rc_t rc = kOkRC ;
if ( vN > abuf - > frameN )
rc = cwLogError ( kInvalidArgRC , " Cannot copy source vector of length %i into an abuf of length %i. " , vN , abuf - > frameN ) ;
else
if ( chIdx > abuf - > chN )
rc = cwLogError ( kInvalidArgRC , " The abuf destination channel %i is out of range. " , chIdx ) ;
else
vop : : copy ( abuf - > buf + ( chIdx * abuf - > frameN ) , v , vN ) ;
return rc ;
}
const cw : : flow : : sample_t * cw : : flow : : abuf_get_channel ( abuf_t * abuf , unsigned chIdx )
{
assert ( abuf - > buf ! = nullptr ) ;
return abuf - > buf + ( chIdx * abuf - > frameN ) ;
}
2023-01-05 12:22:37 +00:00
cw : : flow : : fbuf_t * cw : : flow : : fbuf_create ( srate_t srate , unsigned chN , const unsigned * maxBinN_V , const unsigned * binN_V , const unsigned * hopSmpN_V , const fd_real_t * * magV , const fd_real_t * * phsV , const fd_real_t * * hzV )
2021-08-15 20:07:12 +00:00
{
2022-12-27 23:09:31 +00:00
for ( unsigned i = 0 ; i < chN ; + + i )
if ( binN_V [ i ] > maxBinN_V [ i ] )
{
cwLogError ( kInvalidArgRC , " A channel bin count (%i) execeeds the max bin count (%i). " , binN_V [ i ] , maxBinN_V [ i ] ) ;
return nullptr ; ;
}
2022-12-05 22:19:58 +00:00
2021-08-15 20:07:12 +00:00
fbuf_t * f = mem : : allocZ < fbuf_t > ( ) ;
2022-12-05 22:19:58 +00:00
f - > srate = srate ;
f - > chN = chN ;
2022-12-27 23:09:31 +00:00
f - > maxBinN_V = mem : : allocZ < unsigned > ( chN ) ;
f - > binN_V = mem : : allocZ < unsigned > ( chN ) ;
2022-11-11 18:51:06 +00:00
f - > hopSmpN_V = mem : : allocZ < unsigned > ( chN ) ;
2023-01-05 12:22:37 +00:00
f - > magV = mem : : allocZ < fd_real_t * > ( chN ) ;
f - > phsV = mem : : allocZ < fd_real_t * > ( chN ) ;
f - > hzV = mem : : allocZ < fd_real_t * > ( chN ) ;
2022-12-05 22:19:58 +00:00
f - > readyFlV = mem : : allocZ < bool > ( chN ) ;
2021-08-15 20:07:12 +00:00
2022-12-05 22:19:58 +00:00
vop : : copy ( f - > binN_V , binN_V , chN ) ;
2022-12-27 23:09:31 +00:00
vop : : copy ( f - > maxBinN_V , maxBinN_V , chN ) ;
vop : : copy ( f - > hopSmpN_V , hopSmpN_V , chN ) ;
2022-11-11 18:51:06 +00:00
2021-08-15 20:07:12 +00:00
if ( magV ! = nullptr | | phsV ! = nullptr | | hzV ! = nullptr )
{
for ( unsigned chIdx = 0 ; chIdx < chN ; + + chIdx )
2022-11-11 18:51:06 +00:00
{
2023-01-05 12:22:37 +00:00
f - > magV [ chIdx ] = ( fd_real_t * ) magV [ chIdx ] ;
f - > phsV [ chIdx ] = ( fd_real_t * ) phsV [ chIdx ] ;
f - > hzV [ chIdx ] = ( fd_real_t * ) hzV [ chIdx ] ;
2021-08-15 20:07:12 +00:00
}
}
else
{
2022-12-27 23:09:31 +00:00
unsigned maxTotalBinsN = vop : : sum ( maxBinN_V , chN ) ;
2023-01-05 12:22:37 +00:00
fd_real_t * buf = mem : : allocZ < fd_real_t > ( kFbufVectN * maxTotalBinsN ) ;
fd_real_t * m = buf ;
2022-12-05 22:19:58 +00:00
for ( unsigned chIdx = 0 ; chIdx < chN ; + + chIdx )
2021-08-15 20:07:12 +00:00
{
2022-12-05 22:19:58 +00:00
f - > magV [ chIdx ] = m + 0 * f - > binN_V [ chIdx ] ;
f - > phsV [ chIdx ] = m + 1 * f - > binN_V [ chIdx ] ;
f - > hzV [ chIdx ] = m + 2 * f - > binN_V [ chIdx ] ;
2022-12-27 23:09:31 +00:00
m + = f - > maxBinN_V [ chIdx ] ;
assert ( m < = buf + kFbufVectN * maxTotalBinsN ) ;
2021-08-15 20:07:12 +00:00
}
2022-12-05 22:19:58 +00:00
2021-08-15 20:07:12 +00:00
f - > buf = buf ;
}
2022-12-05 22:19:58 +00:00
return f ;
}
2023-01-05 12:22:37 +00:00
cw : : flow : : fbuf_t * cw : : flow : : fbuf_create ( srate_t srate , unsigned chN , unsigned maxBinN , unsigned binN , unsigned hopSmpN , const fd_real_t * * magV , const fd_real_t * * phsV , const fd_real_t * * hzV )
2022-12-05 22:19:58 +00:00
{
2022-12-27 23:09:31 +00:00
unsigned maxBinN_V [ chN ] ;
2022-12-05 22:19:58 +00:00
unsigned binN_V [ chN ] ;
unsigned hopSmpN_V [ chN ] ;
2022-12-27 23:09:31 +00:00
vop : : fill ( maxBinN_V , chN , maxBinN ) ;
2022-12-05 22:19:58 +00:00
vop : : fill ( binN_V , chN , binN ) ;
vop : : fill ( hopSmpN_V , chN , binN ) ;
2022-12-27 23:09:31 +00:00
return fbuf_create ( srate , chN , maxBinN_V , binN_V , hopSmpN_V , magV , phsV , hzV ) ;
2022-12-05 22:19:58 +00:00
2021-08-15 20:07:12 +00:00
}
2022-12-05 22:19:58 +00:00
2022-01-22 14:49:45 +00:00
void cw : : flow : : fbuf_destroy ( fbuf_t * & fbuf )
2021-08-15 20:07:12 +00:00
{
if ( fbuf = = nullptr )
return ;
2022-11-11 18:51:06 +00:00
2022-12-27 23:09:31 +00:00
mem : : release ( fbuf - > maxBinN_V ) ;
2022-11-11 18:51:06 +00:00
mem : : release ( fbuf - > binN_V ) ;
mem : : release ( fbuf - > hopSmpN_V ) ;
2023-03-17 21:44:29 +00:00
mem : : release ( fbuf - > magV ) ;
mem : : release ( fbuf - > phsV ) ;
mem : : release ( fbuf - > hzV ) ;
2021-08-15 20:07:12 +00:00
mem : : release ( fbuf - > buf ) ;
2021-08-23 02:41:33 +00:00
mem : : release ( fbuf - > readyFlV ) ;
2021-08-15 20:07:12 +00:00
mem : : release ( fbuf ) ;
}
2021-08-23 02:41:33 +00:00
cw : : flow : : fbuf_t * cw : : flow : : fbuf_duplicate ( const fbuf_t * src )
{
2022-12-27 23:09:31 +00:00
fbuf_t * fbuf = fbuf_create ( src - > srate , src - > chN , src - > maxBinN_V , src - > binN_V , src - > hopSmpN_V ) ;
2022-12-05 22:19:58 +00:00
2021-08-23 02:41:33 +00:00
for ( unsigned i = 0 ; i < fbuf - > chN ; + + i )
{
2022-12-27 23:09:31 +00:00
fbuf - > maxBinN_V [ i ] = src - > maxBinN_V [ i ] ;
2022-11-11 18:51:06 +00:00
fbuf - > binN_V [ i ] = src - > binN_V [ i ] ;
fbuf - > hopSmpN_V [ i ] = src - > hopSmpN_V [ i ] ;
vop : : copy ( fbuf - > magV [ i ] , src - > magV [ i ] , fbuf - > binN_V [ i ] ) ;
vop : : copy ( fbuf - > phsV [ i ] , src - > phsV [ i ] , fbuf - > binN_V [ i ] ) ;
vop : : copy ( fbuf - > hzV [ i ] , src - > hzV [ i ] , fbuf - > binN_V [ i ] ) ;
2021-08-23 02:41:33 +00:00
}
return fbuf ;
}
2021-08-15 20:07:12 +00:00
unsigned cw : : flow : : value_type_label_to_flag ( const char * s )
{
unsigned flags = labelToId ( typeLabelFlagsA , s , kInvalidTFl ) ;
if ( flags = = kInvalidTFl )
cwLogError ( kInvalidArgRC , " Invalid type flag: '%s' " , cwStringNullGuard ( s ) ) ;
return flags ;
}
cw : : flow : : class_desc_t * cw : : flow : : class_desc_find ( flow_t * p , const char * label )
{
for ( unsigned i = 0 ; i < p - > classDescN ; + + i )
if ( textCompare ( p - > classDescA [ i ] . label , label ) = = 0 )
return p - > classDescA + i ;
return nullptr ;
}
cw : : flow : : var_desc_t * cw : : flow : : var_desc_find ( class_desc_t * cd , const char * label )
{
var_desc_t * vd = cd - > varDescL ;
for ( ; vd ! = nullptr ; vd = vd - > link )
if ( textCompare ( vd - > label , label ) = = 0 )
return vd ;
return nullptr ;
}
cw : : rc_t cw : : flow : : var_desc_find ( class_desc_t * cd , const char * label , var_desc_t * & vdRef )
{
if ( ( vdRef = var_desc_find ( cd , label ) ) = = nullptr )
return cwLogError ( kInvalidArgRC , " The variable desc. named '%s' could not be found on the class '%s'. " , label , cd - > label ) ;
return kOkRC ;
}
2021-08-23 02:41:33 +00:00
void cw : : flow : : class_dict_print ( flow_t * p )
2021-08-15 20:07:12 +00:00
{
for ( unsigned i = 0 ; i < p - > classDescN ; + + i )
{
class_desc_t * cd = p - > classDescA + i ;
var_desc_t * vd = cd - > varDescL ;
printf ( " %s \n " , cwStringNullGuard ( cd - > label ) ) ;
for ( ; vd ! = nullptr ; vd = vd - > link )
{
2022-11-11 18:09:07 +00:00
const char * srcFlStr = vd - > flags & kSrcVarFl ? " src " : " " ;
const char * srcOptFlStr = vd - > flags & kSrcOptVarFl ? " srcOpt " : " " ;
2021-08-15 20:07:12 +00:00
2022-11-11 18:09:07 +00:00
printf ( " %10s 0x%08x %s %s %s \n " , cwStringNullGuard ( vd - > label ) , vd - > type , srcFlStr , srcOptFlStr , cwStringNullGuard ( vd - > docText ) ) ;
2021-08-15 20:07:12 +00:00
}
}
}
void cw : : flow : : network_print ( flow_t * p )
{
for ( instance_t * inst = p - > network_head ; inst ! = nullptr ; inst = inst - > link )
instance_print ( inst ) ;
}
cw : : flow : : instance_t * cw : : flow : : instance_find ( flow_t * p , const char * inst_label )
{
for ( instance_t * inst = p - > network_head ; inst ! = nullptr ; inst = inst - > link )
if ( textCompare ( inst_label , inst - > label ) = = 0 )
return inst ;
return nullptr ;
}
cw : : rc_t cw : : flow : : instance_find ( flow_t * p , const char * inst_label , instance_t * & instPtrRef )
{
rc_t rc = kOkRC ;
if ( ( instPtrRef = instance_find ( p , inst_label ) ) ! = nullptr )
return rc ;
return cwLogError ( kInvalidArgRC , " The instance '%s' was not found. " , inst_label ) ;
}
2021-12-11 20:17:11 +00:00
cw : : flow : : external_device_t * cw : : flow : : external_device_find ( flow_t * p , const char * device_label , unsigned typeId , unsigned inOrOutFl )
{
for ( unsigned i = 0 ; i < p - > deviceN ; + + i )
if ( cw : : textIsEqual ( p - > deviceA [ i ] . label , device_label ) & & p - > deviceA [ i ] . typeId = = typeId & & cwIsFlag ( p - > deviceA [ i ] . flags , inOrOutFl ) )
return p - > deviceA + i ;
cwLogError ( kInvalidArgRC , " The %s device named '%s' could not be found. " , cwIsFlag ( inOrOutFl , kInFl ) ? " in " : " out " , device_label ) ;
return nullptr ;
}
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
void cw : : flow : : instance_print ( instance_t * inst )
{
printf ( " %s \n " , inst - > label ) ;
2021-08-23 02:41:33 +00:00
for ( variable_t * var = inst - > varL ; var ! = nullptr ; var = var - > var_link )
if ( var - > chIdx = = kAnyChIdx )
for ( variable_t * v0 = var ; v0 ! = nullptr ; v0 = v0 - > ch_link )
_var_print ( v0 ) ;
if ( inst - > class_desc - > members - > report )
inst - > class_desc - > members - > report ( inst ) ;
}
2021-12-11 20:17:11 +00:00
2021-08-23 02:41:33 +00:00
void cw : : flow : : _var_destroy ( variable_t * var )
{
if ( var ! = nullptr )
2021-08-15 20:07:12 +00:00
{
2022-01-22 14:49:45 +00:00
for ( unsigned i = 0 ; i < kLocalValueN ; + + i )
_value_release ( var - > local_value + i ) ;
2021-08-23 02:41:33 +00:00
mem : : release ( var - > label ) ;
mem : : release ( var ) ;
2021-08-15 20:07:12 +00:00
}
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_create ( instance_t * inst , const char * var_label , unsigned id , unsigned chIdx , const object_t * value_cfg , variable_t * & varRef )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
2021-08-23 02:41:33 +00:00
rc = _var_create ( inst , var_label , id , chIdx , value_cfg , varRef ) ;
return rc ;
}
2021-12-30 02:52:46 +00:00
cw : : rc_t cw : : flow : : var_channelize ( instance_t * inst , const char * var_label , unsigned chIdx , const object_t * value_cfg , unsigned vid , variable_t * & varRef )
2021-08-23 02:41:33 +00:00
{
rc_t rc = kOkRC ;
2021-08-15 20:07:12 +00:00
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
variable_t * base_var = nullptr ;
varRef = nullptr ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
if ( ( base_var = _var_find_on_label_and_ch ( inst , var_label , kAnyChIdx ) ) = = nullptr )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc = cwLogError ( kInvalidStateRC , " The base ('any') channel variable could not be located on '%s.%s'. " , inst - > label , var_label ) ;
2021-08-15 20:07:12 +00:00
goto errLabel ;
}
2021-08-23 02:41:33 +00:00
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// locate the variable with the stated chIdx
var = _var_find_on_label_and_ch ( inst , var_label , chIdx ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// 'src' variables cannot be channelized
if ( cwIsFlag ( base_var - > varDesc - > flags , kSrcVarFl ) )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc = cwLogError ( rc , " 'src' variables cannot be channelized. " ) ;
2021-08-15 20:07:12 +00:00
goto errLabel ;
}
2021-08-23 02:41:33 +00:00
// if the requested var was not found then create a new variable with the requested channel index
if ( var = = nullptr & & chIdx ! = kAnyChIdx )
{
// create the channelized var
2021-12-30 02:52:46 +00:00
if ( ( rc = _var_create ( inst , var_label , vid , chIdx , value_cfg , var ) ) ! = kOkRC )
2021-08-23 02:41:33 +00:00
goto errLabel ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// if no value was set then set the value from the 'any' channel
if ( value_cfg = = nullptr )
{
// Set the value of the new variable to the value of the 'any' channel
2022-01-22 14:49:45 +00:00
_value_duplicate ( var - > local_value [ var - > local_value_idx ] , base_var - > local_value [ base_var - > local_value_idx ] ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// If the 'any' channel value was set to point to it's local value then do same with this value
2022-01-22 14:49:45 +00:00
if ( base_var - > local_value + base_var - > local_value_idx = = base_var - > value )
var - > value = var - > local_value + var - > local_value_idx ;
2021-08-23 02:41:33 +00:00
}
}
else
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
// a correctly channelized var was found - but we still may need to set the value
if ( value_cfg ! = nullptr )
{
rc = _set_var_value_from_cfg ( var , value_cfg ) ;
}
else
{
cwLogWarning ( " An existing var (%s.%s ch:%i) was specified for channelizing but no value was provided. " , inst - > label , var_label , chIdx ) ;
}
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
assert ( var ! = nullptr ) ;
varRef = var ;
errLabel :
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " Channelize failed for variable '%s' on instance '%s' ch:%i. " , var_label , inst - > label , chIdx ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2021-08-23 02:41:33 +00:00
bool cw : : flow : : var_exists ( instance_t * inst , const char * label , unsigned chIdx )
{ return _var_find_on_label_and_ch ( inst , label , chIdx ) ! = nullptr ; }
2021-08-15 20:07:12 +00:00
2022-11-11 18:09:07 +00:00
bool cw : : flow : : var_has_value ( instance_t * inst , const char * label , unsigned chIdx )
{
variable_t * varPtr = nullptr ;
rc_t rc ;
if ( ( rc = var_find ( inst , label , chIdx , varPtr ) ) ! = kOkRC )
return false ;
return varPtr - > value ! = nullptr ;
}
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_find ( instance_t * inst , unsigned vid , unsigned chIdx , variable_t * & varRef )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc = kOkRC ;
unsigned idx = kInvalidIdx ;
2021-08-15 20:07:12 +00:00
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
varRef = nullptr ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// if the varMapA[] has not yet been formed (we are inside the instance constructor) then do a slow lookup of the variable
if ( inst - > varMapA = = nullptr )
{
if ( ( rc = _var_find_on_vid_and_ch ( inst , vid , chIdx , var ) ) ! = kOkRC )
goto errLabel ;
}
else
{
// otherwise do a fast lookup using inst->varMapA[]
if ( ( rc = _var_map_id_to_index ( inst , vid , chIdx , idx ) ) = = kOkRC & & ( idx ! = kInvalidIdx ) )
var = inst - > varMapA [ idx ] ;
else
{
rc = cwLogError ( kInvalidIdRC , " The index of variable vid:%i chIdx:%i on instance '%s' could not be calculated and the variable value could not be retrieved. " , vid , chIdx , inst - > label ) ;
goto errLabel ;
}
}
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
// if we get here var must be non-null
assert ( var ! = nullptr & & rc = = kOkRC ) ;
varRef = var ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
errLabel :
2021-08-15 20:07:12 +00:00
return rc ;
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_find ( instance_t * inst , const char * label , unsigned chIdx , variable_t * & vRef )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
variable_t * var ;
vRef = nullptr ;
2022-01-22 14:49:45 +00:00
if ( ( var = _var_find_on_label_and_ch ( inst , label , chIdx ) ) ! = nullptr )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
vRef = var ;
return kOkRC ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
return cwLogError ( kInvalidIdRC , " The instance '%s' does not have a variable named '%s'. " , inst - > label , label ) ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_find ( instance_t * inst , const char * label , unsigned chIdx , const variable_t * & vRef )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
variable_t * v = nullptr ;
rc_t rc = var_find ( inst , label , chIdx , v ) ;
vRef = v ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2022-01-22 14:49:45 +00:00
cw : : rc_t cw : : flow : : var_channel_count ( instance_t * inst , const char * label , unsigned & chCntRef )
{
rc_t rc = kOkRC ;
const variable_t * var = nullptr ;
if ( ( rc = var_find ( inst , label , kAnyChIdx , var ) ) ! = kOkRC )
return cwLogError ( rc , " Channel count was not available because the variable '%s.%s' does not exist. " , cwStringNullGuard ( inst - > label ) , cwStringNullGuard ( label ) ) ;
return var_channel_count ( var , chCntRef ) ;
}
cw : : rc_t cw : : flow : : var_channel_count ( const variable_t * var , unsigned & chCntRef )
{
rc_t rc = kOkRC ;
const variable_t * v ;
chCntRef = 0 ;
if ( ( rc = var_find ( var - > inst , var - > label , kAnyChIdx , v ) ) ! = kOkRC )
{
rc = cwLogError ( kInvalidStateRC , " The base channel variable instance could not be found for the variable '%s.%s'. " , var - > inst - > label , var - > label ) ;
goto errLabel ;
}
for ( v = v - > ch_link ; v ! = nullptr ; v = v - > ch_link )
chCntRef + = 1 ;
errLabel :
return rc ;
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_register ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , const object_t * value_cfg , variable_t * & varRef )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc = kOkRC ;
2021-08-15 20:07:12 +00:00
variable_t * var = nullptr ;
varRef = nullptr ;
2021-08-23 02:41:33 +00:00
// TODO: check for duplicate 'vid'-'chIdx' pairs on this instance
// The concatenation of 'vid' and 'chIdx' should be unique
// if an exact match to label/chIdx was found
if ( ( var = _var_find_on_label_and_ch ( inst , var_label , chIdx ) ) ! = nullptr )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
// if a value was given - then update the value
if ( value_cfg ! = nullptr )
if ( ( rc = _set_var_value_from_cfg ( var , value_cfg ) ) ! = kOkRC )
goto errLabel ;
}
else // an exact match was not found - channelize the variable
{
2021-12-30 02:52:46 +00:00
if ( ( rc = var_channelize ( inst , var_label , chIdx , value_cfg , vid , var ) ) ! = kOkRC )
2021-08-23 02:41:33 +00:00
goto errLabel ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
var - > vid = vid ;
varRef = var ;
2021-12-30 02:52:46 +00:00
if ( ( var = _var_find_on_label_and_ch ( inst , var_label , kAnyChIdx ) ) ! = nullptr )
var - > vid = vid ;
else
rc = cwLogError ( kInvalidStateRC , " The variable '%s' instance '%s' has no base channel. " , var_label , inst - > label , chIdx ) ;
2021-08-23 02:41:33 +00:00
errLabel :
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " Registration failed on variable '%s' instance '%s' ch: %i. " , var_label , inst - > label , chIdx ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_register_and_set ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , variable_t * & varRef )
{
return var_register ( inst , var_label , vid , chIdx , nullptr , varRef ) ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_register_and_set ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , srate_t srate , unsigned chN , unsigned frameN )
2021-08-15 20:07:12 +00:00
{
2021-08-23 02:41:33 +00:00
rc_t rc = kOkRC ;
abuf_t * abuf ;
if ( ( abuf = abuf_create ( srate , chN , frameN ) ) = = nullptr )
return cwLogError ( kOpFailRC , " abuf create failed on instance:'%s' variable:'%s'. " , inst - > label , var_label ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_register_and_set ( inst , var_label , vid , chIdx , abuf ) ) ! = kOkRC )
abuf_destroy ( abuf ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2023-01-05 12:22:37 +00:00
cw : : rc_t cw : : flow : : var_register_and_set ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , srate_t srate , unsigned chN , const unsigned * maxBinN_V , const unsigned * binN_V , const unsigned * hopSmpN_V , const fd_real_t * * magV , const fd_real_t * * phsV , const fd_real_t * * hzV )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
2021-08-23 02:41:33 +00:00
fbuf_t * fbuf ;
2022-12-27 23:09:31 +00:00
if ( ( fbuf = fbuf_create ( srate , chN , maxBinN_V , binN_V , hopSmpN_V , magV , phsV , hzV ) ) = = nullptr )
2021-08-23 02:41:33 +00:00
return cwLogError ( kOpFailRC , " fbuf create failed on instance:'%s' variable:'%s'. " , inst - > label , var_label ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_register_and_set ( inst , var_label , vid , chIdx , fbuf ) ) ! = kOkRC )
fbuf_destroy ( fbuf ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2023-01-05 12:22:37 +00:00
cw : : rc_t cw : : flow : : var_register_and_set ( instance_t * inst , const char * var_label , unsigned vid , unsigned chIdx , srate_t srate , unsigned chN , unsigned maxBinN , unsigned binN , unsigned hopSmpN , const fd_real_t * * magV , const fd_real_t * * phsV , const fd_real_t * * hzV )
2022-12-05 22:19:58 +00:00
{
2022-12-27 23:09:31 +00:00
unsigned maxBinN_V [ chN ] ;
2022-12-05 22:19:58 +00:00
unsigned binN_V [ chN ] ;
unsigned hopSmpN_V [ chN ] ;
2022-12-27 23:09:31 +00:00
vop : : fill ( maxBinN_V , chN , maxBinN ) ;
2022-12-05 22:19:58 +00:00
vop : : fill ( binN_V , chN , binN ) ;
vop : : fill ( hopSmpN_V , chN , hopSmpN ) ;
2022-12-27 23:09:31 +00:00
return var_register_and_set ( inst , var_label , vid , chIdx , srate , chN , maxBinN_V , binN_V , hopSmpN_V , magV , phsV , hzV ) ;
2022-12-05 22:19:58 +00:00
}
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , bool & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , uint_t & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , int_t & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , float & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , double & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , const char * & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , const abuf_t * & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( variable_t * var , abuf_t * & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( const variable_t * var , const fbuf_t * & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_get ( variable_t * var , fbuf_t * & valRef )
2022-01-22 14:49:45 +00:00
{ return _val_get_driver ( var , valRef ) ; }
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_set ( instance_t * inst , unsigned vid , unsigned chIdx , bool val )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_find_to_set ( inst , vid , chIdx , kBoolTFl , var ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kBoolTFl , val ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
return rc ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_set ( instance_t * inst , unsigned vid , unsigned chIdx , uint_t val )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_find_to_set ( inst , vid , chIdx , kUIntTFl , var ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kUIntTFl , val ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
return rc ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_set ( instance_t * inst , unsigned vid , unsigned chIdx , int_t val )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_find_to_set ( inst , vid , chIdx , kIntTFl , var ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kIntTFl , val ) ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
return rc ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_set ( instance_t * inst , unsigned vid , unsigned chIdx , float val )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_find_to_set ( inst , vid , chIdx , kFloatTFl , var ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kFloatTFl , val ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_set ( instance_t * inst , unsigned vid , unsigned chIdx , double val )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_find_to_set ( inst , vid , chIdx , kDoubleTFl , var ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kDoubleTFl , val ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2021-08-23 02:41:33 +00:00
cw : : rc_t cw : : flow : : var_set ( instance_t * inst , unsigned vid , unsigned chIdx , const char * val )
2021-08-15 20:07:12 +00:00
{
rc_t rc = kOkRC ;
variable_t * var = nullptr ;
2021-08-23 02:41:33 +00:00
if ( ( rc = _var_find_to_set ( inst , vid , chIdx , kStringTFl , var ) ) = = kOkRC )
2021-12-30 02:52:46 +00:00
_var_set_driver ( var , kStringTFl , val ) ;
2021-08-15 20:07:12 +00:00
return rc ;
}
2021-08-23 02:41:33 +00:00
const cw : : flow : : preset_t * cw : : flow : : class_preset_find ( class_desc_t * cd , const char * preset_label )
2021-08-15 20:07:12 +00:00
{
const preset_t * pr ;
2021-08-23 02:41:33 +00:00
for ( pr = cd - > presetL ; pr ! = nullptr ; pr = pr - > link )
if ( textCompare ( pr - > label , preset_label ) = = 0 )
return pr ;
2021-08-15 20:07:12 +00:00
2021-08-23 02:41:33 +00:00
return nullptr ;
2021-08-15 20:07:12 +00:00
}
2021-08-23 02:41:33 +00:00