libcw/cwFlowProc.cpp
kevin b57693f4e4 cwFlow,cwFlowNet,cwFlowTypes,cwFlowProc : Many changes.
Added ability to set types at proc instantiation time.
Added ability to have multi-type variables which get resolved at proc. instantiation time.
Added ability to proc 'log' attribute to print selected variables as they change.
Values are now coerced to the variable type in var_set().
Added new proc's : list,add,preset.
2024-04-30 19:58:10 -04:00

4861 lines
135 KiB
C++

#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwText.h"
#include "cwObject.h"
#include "cwAudioFile.h"
#include "cwVectOps.h"
#include "cwMtx.h"
#include "cwDspTypes.h" // srate_t, sample_t, coeff_t, ...
#include "cwTime.h"
#include "cwMidiDecls.h"
#include "cwFlowDecl.h"
#include "cwFlow.h"
#include "cwFlowTypes.h"
#include "cwFlowNet.h"
#include "cwFlowProc.h"
#include "cwFile.h"
#include "cwMath.h"
#include "cwDsp.h"
#include "cwAudioTransforms.h"
#include "cwDspTransforms.h"
namespace cw
{
namespace flow
{
//------------------------------------------------------------------------------------------------------------------
//
// Template
//
namespace template_proc
{
typedef struct
{
} inst_t;
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = mem::allocZ<inst_t>();
proc->userPtr = p;
// Custom create code goes here
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
// Custom clean-up code goes here
mem::release(p);
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* proc )
{
rc_t rc = kOkRC;
//inst_t* p = (inst_t*)proc->userPtr;
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// poly
//
namespace poly
{
enum
{
kCountPId,
kOrderPId,
};
typedef struct
{
unsigned count;
network_t net;
network_order_id_t orderId;
} inst_t;
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* inst = mem::allocZ<inst_t>();
const object_t* networkCfg = nullptr;
const char* order_label = nullptr;
proc->userPtr = inst;
if((rc = var_register_and_get( proc, kAnyChIdx,
kCountPId, "count", kBaseSfxId, inst->count,
kOrderPId, "order", kBaseSfxId, order_label )) != kOkRC )
goto errLabel;
if( inst->count == 0 )
{
cwLogWarning("The 'poly' %s:%i was given a count of 0.",proc->label,proc->label_sfx_id);
goto errLabel;
}
if((rc = proc->proc_cfg->getv("network",networkCfg)) != kOkRC )
{
rc = cwLogError(rc,"The 'network' cfg. was not found.");
goto errLabel;
}
// get the network exec. order type
if( textIsEqual(order_label,"net") )
inst->orderId = kNetFirstPolyOrderId;
else
{
if( textIsEqual(order_label,"proc") )
inst->orderId = kProcFirstPolyOrderId;
else
{
rc = cwLogError(kInvalidArgRC,"'%s' is not one of the valid order types (i.e. 'net','proc').",order_label);
goto errLabel;
}
}
if((rc = network_create(proc->ctx,networkCfg,inst->net,inst->count )) != kOkRC )
{
rc = cwLogError(rc,"Creation failed on the internal network.");
goto errLabel;
}
// Set the internal net pointer in the base proc instance
// so that network based utilities can scan it
proc->internal_net = &inst->net;
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
inst_t* p = (inst_t*)proc->userPtr;
network_destroy(p->net);
mem::release( proc->userPtr );
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
inst_t* p = (inst_t*)ctx->userPtr;
rc_t rc = kOkRC;
if((rc = exec_cycle(p->net)) != kOkRC )
{
rc = cwLogError(rc,"poly internal network exec failed.");
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// balance
//
namespace balance
{
enum
{
kInPId,
kOutPId,
kInvOutPId
};
typedef struct
{
coeff_t value;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
coeff_t in_value = 0.5;
ctx->userPtr = mem::allocZ<inst_t>();
if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", kBaseSfxId, in_value )) != kOkRC )
goto errLabel;
if((rc = var_register_and_set( ctx, kAnyChIdx,
kOutPId, "out", kBaseSfxId, in_value,
kInvOutPId, "inv_out", kBaseSfxId, (coeff_t)(1.0-in_value) )) != kOkRC )
{
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
mem::release( ctx->userPtr );
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)(ctx->userPtr);
coeff_t value = 1;
var_get(ctx, kInPId, kAnyChIdx, value);
var_set(ctx, kOutPId, kAnyChIdx, value);
var_set(ctx, kInvOutPId, kAnyChIdx, (coeff_t)(1.0 - value) );
if( inst->value != value )
{
inst->value = value;
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// midi_in
//
namespace midi_in
{
enum
{
kDevLabelPId,
kPortLabelPId,
kOutPId
};
typedef struct
{
midi::ch_msg_t* buf;
unsigned bufN;
bool dev_filt_fl;
bool port_filt_fl;
external_device_t* ext_dev;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const char* dev_label = nullptr;
const char* port_label = nullptr;
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// Register variable and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx,
kDevLabelPId, "dev_label", kBaseSfxId, dev_label,
kPortLabelPId, "port_label", kBaseSfxId, port_label )) != kOkRC )
{
goto errLabel;
}
if((rc = var_register( ctx, kAnyChIdx, kOutPId, "out", kBaseSfxId)) != kOkRC )
{
goto errLabel;
}
inst->dev_filt_fl = true;
inst->port_filt_fl = true;
if( textIsEqual(dev_label,"<all>") )
{
inst->dev_filt_fl = false;
dev_label = nullptr;
}
if( textIsEqual(dev_label,"<all>") )
{
inst->port_filt_fl = false;
port_label = nullptr;
}
if((inst->ext_dev = external_device_find( ctx->ctx, dev_label, kMidiDevTypeId, kInFl, port_label )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The MIDI input device '%s' port '%s' could not be found.", cwStringNullGuard(dev_label), cwStringNullGuard(port_label));
goto errLabel;
}
// Allocate a buffer large enough to hold the max. number of messages arriving on a single call to exec().
inst->bufN = inst->ext_dev->u.m.maxMsgCnt;
inst->buf = mem::allocZ<midi::ch_msg_t>( inst->bufN );
// create one output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, 0 );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst->buf);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; }
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mbuf_t* mbuf = nullptr;
// get the output variable
if((rc = var_get(ctx,kOutPId,kAnyChIdx,mbuf)) != kOkRC )
{
rc = cwLogError(kInvalidStateRC,"The MIDI file instance '%s' does not have a valid MIDI output buffer.",ctx->label);
}
else
{
// if the device filter is not set
if( !inst->dev_filt_fl)
{
mbuf->msgA = inst->ext_dev->u.m.msgArray;
mbuf->msgN = inst->ext_dev->u.m.msgCnt;
}
else // the device filter is set
{
const midi::ch_msg_t* m = inst->ext_dev->u.m.msgArray;
unsigned j = 0;
for(unsigned i=0; i<inst->ext_dev->u.m.msgCnt && j<inst->bufN; ++i)
if( m->devIdx == inst->ext_dev->ioDevIdx && (!inst->port_filt_fl || m->portIdx == inst->ext_dev->ioPortIdx) )
inst->buf[j++] = m[i];
mbuf->msgN = j;
mbuf->msgA = inst->buf;
}
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// midi_out
//
namespace midi_out
{
enum
{
kInPId,
kDevLabelPId,
kPortLabelPId
};
typedef struct
{
external_device_t* ext_dev;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC; //
inst_t* inst = mem::allocZ<inst_t>(); //
const char* dev_label = nullptr;
const char* port_label = nullptr;
mbuf_t* mbuf = nullptr;
ctx->userPtr = inst;
// Register variables and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx,
kDevLabelPId, "dev_label", kBaseSfxId, dev_label,
kPortLabelPId,"port_label", kBaseSfxId, port_label,
kInPId, "in", kBaseSfxId, mbuf)) != kOkRC )
{
goto errLabel;
}
if((inst->ext_dev = external_device_find( ctx->ctx, dev_label, kMidiDevTypeId, kOutFl, port_label )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(dev_label));
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const mbuf_t* src_mbuf = nullptr;
if((rc = var_get(ctx,kInPId,kAnyChIdx,src_mbuf)) != kOkRC )
rc = cwLogError(kInvalidStateRC,"The MIDI output instance '%s' does not have a valid input connection.",ctx->label);
else
{
for(unsigned i=0; i<src_mbuf->msgN; ++i)
{
const midi::ch_msg_t* m = src_mbuf->msgA + i;
inst->ext_dev->u.m.sendTripleFunc( inst->ext_dev, m->ch, m->status, m->d0, m->d1 );
}
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_in
//
namespace audio_in
{
enum
{
kDevLabelPId,
kOutPId
};
typedef struct
{
const char* dev_label;
external_device_t* ext_dev;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// Register variable and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx, kDevLabelPId, "dev_label", kBaseSfxId, inst->dev_label )) != kOkRC )
{
goto errLabel;
}
if((inst->ext_dev = external_device_find( ctx->ctx, inst->dev_label, kAudioDevTypeId, kInFl )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The audio input device description '%s' could not be found.", cwStringNullGuard(inst->dev_label));
goto errLabel;
}
// create one output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, inst->ext_dev->u.a.abuf->srate, inst->ext_dev->u.a.abuf->chN, ctx->ctx->framesPerCycle );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
abuf_t* abuf = nullptr;
// verify that a source buffer exists
if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC )
{
rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",ctx->label);
}
else
{
unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, abuf->chN );
unsigned frameN = std::min(inst->ext_dev->u.a.abuf->frameN, abuf->frameN );
memcpy(abuf->buf,inst->ext_dev->u.a.abuf->buf, frameN*chN*sizeof(sample_t));
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_out
//
namespace audio_out
{
enum
{
kInPId,
kDevLabelPId,
};
typedef struct
{
const char* dev_label;
external_device_t* ext_dev;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC; //
inst_t* inst = mem::allocZ<inst_t>(); //
const abuf_t* src_abuf = nullptr;
ctx->userPtr = inst;
// Register variables and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx,
kDevLabelPId, "dev_label", kBaseSfxId, inst->dev_label,
kInPId, "in", kBaseSfxId, src_abuf)) != kOkRC )
{
goto errLabel;
}
if((inst->ext_dev = external_device_find( ctx->ctx, inst->dev_label, kAudioDevTypeId, kOutFl )) == nullptr )
{
rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(inst->dev_label));
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* src_abuf = nullptr;
if((rc = var_get(ctx,kInPId,kAnyChIdx,src_abuf)) != kOkRC )
rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label);
else
{
unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, src_abuf->chN);
unsigned frameN = std::min(inst->ext_dev->u.a.abuf->frameN, src_abuf->frameN);
unsigned n = chN * frameN;
for(unsigned i=0; i<n; ++i)
inst->ext_dev->u.a.abuf->buf[i] += src_abuf->buf[i];
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_file_in
//
namespace audio_file_in
{
enum
{
kFnamePId,
kEofFlPId,
kOnOffFlPId,
kSeekSecsPId,
kOutPId
};
typedef struct
{
audiofile::handle_t afH;
bool eofFl;
const char* filename;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
audiofile::info_t info;
ftime_t seekSecs;
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
if((rc = var_register( ctx, kAnyChIdx, kOnOffFlPId, "on_off", kBaseSfxId)) != kOkRC )
{
goto errLabel;
}
// Register variable and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx,
kFnamePId, "fname", kBaseSfxId, inst->filename,
kSeekSecsPId, "seekSecs", kBaseSfxId, seekSecs,
kEofFlPId, "eofFl", kBaseSfxId, inst->eofFl )) != kOkRC )
{
goto errLabel;
}
// open the audio file
if((rc = audiofile::open(inst->afH,inst->filename,&info)) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"The audio file '%s' could not be opened.",inst->filename);
goto errLabel;
}
if((rc = seek( inst->afH, (unsigned)lround(seekSecs*info.srate) )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"The audio file '%s' could not seek to offset %f seconds.",seekSecs);
goto errLabel;
}
cwLogInfo("Audio '%s' srate:%f chs:%i frames:%i %f seconds.",inst->filename,info.srate,info.chCnt,info.frameCnt, info.frameCnt/info.srate );
// create one output audio buffer - with the same configuration as the source audio file
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
if((rc = audiofile::close(inst->afH)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The close failed on the audio file '%s'.", cwStringNullGuard(inst->filename) );
}
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
ftime_t seekSecs = 0;
inst_t* inst = (inst_t*)ctx->userPtr;
if((rc = var_get(ctx,kSeekSecsPId,kAnyChIdx,seekSecs)) != kOkRC )
goto errLabel;
if((rc = seek( inst->afH, (unsigned)lround(seekSecs * audiofile::sampleRate(inst->afH) ) )) != kOkRC )
{
rc = cwLogError(kInvalidArgRC,"The audio file '%s' could not seek to offset %f seconds.",seekSecs);
goto errLabel;
}
errLabel:
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
unsigned actualFrameN = 0;
inst_t* inst = (inst_t*)ctx->userPtr;
abuf_t* abuf = nullptr;
bool onOffFl = false;
// get the 'on-off; flag
if((rc = var_get(ctx,kOnOffFlPId,kAnyChIdx,onOffFl)) != kOkRC )
goto errLabel;
// verify that a source buffer exists
if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC )
{
rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",ctx->label);
}
else
{
sample_t* chBuf[ abuf->chN ];
for(unsigned i=0; i<abuf->chN; ++i)
{
chBuf[i] = abuf->buf + (i*abuf->frameN);
// if the on/off flag is not set - then fill the output buffer with zeros
if( !onOffFl )
vop::zero(chBuf[i],abuf->frameN);
}
// if the on/off flag is set then read from audio file
if( onOffFl )
rc = readFloat(inst->afH, abuf->frameN, 0, abuf->chN, chBuf, &actualFrameN );
if( inst->eofFl && actualFrameN == 0)
rc = kEofRC;
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_file_out
//
namespace audio_file_out
{
enum
{
kInPId,
kFnamePId,
kBitsPId
};
typedef struct
{
audiofile::handle_t afH;
const char* filename;
unsigned durSmpN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC; //
unsigned audioFileBits = 0; // set audio file sample format to 'float32'.
inst_t* inst = mem::allocZ<inst_t>(); //
const abuf_t* src_abuf = nullptr;
ctx->userPtr = inst;
// Register variables and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx,
kFnamePId, "fname", kBaseSfxId, inst->filename,
kBitsPId, "bits", kBaseSfxId, audioFileBits,
kInPId, "in", kBaseSfxId, src_abuf )) != kOkRC )
{
goto errLabel;
}
// create the audio file with the same channel count as the incoming signal
if((rc = audiofile::create( inst->afH, inst->filename, src_abuf->srate, audioFileBits, src_abuf->chN)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The audio file create failed on '%s'.",cwStringNullGuard(inst->filename));
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
// close the audio file
if((rc = audiofile::close( inst->afH )) != kOkRC )
{
rc = cwLogError(rc,"Close failed on the audio output file '%s'.",inst->filename);
goto errLabel;
}
mem::release(inst);
errLabel:
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* src_abuf = nullptr;
if((rc = var_get(ctx,kInPId,kAnyChIdx,src_abuf)) != kOkRC )
rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label);
else
{
sample_t* chBuf[ src_abuf->chN ];
for(unsigned i=0; i<src_abuf->chN; ++i)
chBuf[i] = src_abuf->buf + (i*src_abuf->frameN);
if((rc = audiofile::writeFloat(inst->afH, src_abuf->frameN, src_abuf->chN, chBuf )) != kOkRC )
rc = cwLogError(rc,"Audio file write failed on instance: '%s'.", ctx->label );
// print a minutes counter
inst->durSmpN += src_abuf->frameN;
if( inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 )
printf("audio file out: %5.1f min\n", inst->durSmpN/(src_abuf->srate*60));
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_gain
//
namespace audio_gain
{
enum
{
kInPId,
kGainPId,
kOutPId
};
typedef struct inst_str
{
unsigned n;
coeff_t vgain;
coeff_t gain;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; //
ctx->userPtr = mem::allocZ<inst_t>();
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC )
goto errLabel;
// register the gain
for(unsigned i=0; i<abuf->chN; ++i)
if((rc = var_register( ctx, i, kGainPId, "gain", kBaseSfxId )) != kOkRC )
goto errLabel;
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
inst_t* inst = (inst_t*)(ctx->userPtr);
mem::release(inst);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{
coeff_t value = 0;
inst_t* inst = (inst_t*)ctx->userPtr;
var_get(ctx,kGainPId,0,value);
if( inst->vgain != value )
{
inst->vgain = value;
//printf("VALUE GAIN: %s %s : %f\n", ctx->label, var->label, value );
}
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr;
inst_t* inst = (inst_t*)(ctx->userPtr);
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
// for each channel
for(unsigned i=0; i<ibuf->chN; ++i)
{
sample_t* isig = ibuf->buf + i*ibuf->frameN;
sample_t* osig = obuf->buf + i*obuf->frameN;
sample_t gain = 1;
var_get(ctx,kGainPId,i,gain);
// apply the gain
for(unsigned j=0; j<ibuf->frameN; ++j)
osig[j] = gain * isig[j];
if( i==0 && gain != inst->gain )
{
inst->gain = gain;
//printf("EXEC GAIN: %s %f\n",ctx->label,gain);
//instance_print(ctx);
}
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_split
//
namespace audio_split
{
enum {
kInPId,
kSelectPId,
kGainPId,
kOutPId,
};
typedef struct
{
bool* chSelMap; // [ inChCnt ] selected channel map
unsigned outChN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId, abuf )) != kOkRC )
goto errLabel;
if( abuf->chN )
{
unsigned selChN = 0;
inst->chSelMap = mem::allocZ<bool>(abuf->chN);
if((rc = var_channel_count(ctx,"select",kBaseSfxId,selChN)) != kOkRC )
goto errLabel;
// register the gain
for(unsigned i=0; i<abuf->chN; ++i)
{
if( i < selChN )
if((rc = var_register_and_get( ctx, i, kSelectPId, "select", kBaseSfxId, inst->chSelMap[i] )) != kOkRC )
goto errLabel;
if( inst->chSelMap[i] )
{
// register an output gain control
if((rc = var_register( ctx, inst->outChN, kGainPId, "gain", kBaseSfxId)) != kOkRC )
goto errLabel;
// count the number of selected channels to determine the count of output channels
inst->outChN += 1;
}
}
// create the output audio buffer
if( inst->outChN == 0 )
cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label);
else
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN );
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst->chSelMap);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr;
inst_t* inst = (inst_t*)ctx->userPtr;
unsigned outChIdx = 0;
if( inst->outChN )
{
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
// for each channel
for(unsigned i=0; i<ibuf->chN && outChIdx<obuf->chN; ++i)
if( inst->chSelMap[i] )
{
sample_t* isig = ibuf->buf + i * ibuf->frameN;
sample_t* osig = obuf->buf + outChIdx * obuf->frameN;
sample_t gain = 1;
var_get(ctx,kGainPId,outChIdx,gain);
// apply the gain
for(unsigned j=0; j<ibuf->frameN; ++j)
osig[j] = gain * isig[j];
outChIdx += 1;
}
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_duplicate
//
namespace audio_duplicate
{
enum {
kInPId,
kDuplicatePId,
kGainPId,
kOutPId,
};
typedef struct
{
unsigned* chDuplMap; // [ inChN ] duplicate channel map
unsigned outChN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC )
goto errLabel;
if( abuf->chN )
{
inst->chDuplMap = mem::allocZ<unsigned>(abuf->chN);
// register the gain
for(unsigned i=0; i<abuf->chN; ++i)
{
if((rc = var_register_and_get( ctx, i, kDuplicatePId, "duplicate", kBaseSfxId, inst->chDuplMap[i] )) != kOkRC )
goto errLabel;
if( inst->chDuplMap[i] )
{
// register an input gain control
if((rc = var_register( ctx, inst->outChN, kGainPId, "gain", kBaseSfxId)) != kOkRC )
goto errLabel;
// count the number of selected channels to determine the count of output channels
inst->outChN += inst->chDuplMap[i];
}
}
// create the output audio buffer
if( inst->outChN == 0 )
cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label);
else
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN );
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst->chDuplMap);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr;
inst_t* inst = (inst_t*)ctx->userPtr;
unsigned outChIdx = 0;
if( inst->outChN )
{
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
// for each input channel
for(unsigned i=0; i<ibuf->chN && outChIdx<obuf->chN; ++i)
{
sample_t* isig = ibuf->buf + i * ibuf->frameN;
sample_t gain = 1;
var_get(ctx,kGainPId,i,gain);
for(unsigned j=0; j<inst->chDuplMap[i]; ++j )
{
sample_t* osig = obuf->buf + j * obuf->frameN;
// apply the gain
for(unsigned k=0; k<ibuf->frameN; ++k)
osig[k] = gain * isig[k];
outChIdx += 1;
}
}
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_merge
//
namespace audio_merge
{
enum {
kGainPId,
kOutPId,
kInBasePId,
};
typedef struct
{
unsigned srcN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
unsigned outChN = 0;
unsigned frameN = 0;
srate_t srate = 0;
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
for(unsigned i=0; 1; ++i)
{
const abuf_t* abuf = nullptr; //
char label[32];
snprintf(label,31,"in%i",i);
label[31] = 0;
// TODO: allow non-contiguous source labels
// the source labels must be contiguous
if( !var_has_value( ctx, label, kBaseSfxId, kAnyChIdx ) )
break;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,kBaseSfxId, abuf )) != kOkRC )
{
goto errLabel;
}
if( i == 0 )
{
frameN = abuf->frameN;
srate = abuf->srate;
}
else
{
// TODO: check srate and frameN are same as first src
assert( abuf->frameN == frameN );
assert( abuf->srate == srate );
}
inst->srcN += 1;
outChN += abuf->chN;
}
// register the gain
for(unsigned i=0; i<outChN; ++i)
if((rc = var_register( ctx, i, kGainPId, "gain", kBaseSfxId )) != kOkRC )
goto errLabel;
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, outChN, frameN );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; }
unsigned _exec( instance_t* ctx, const abuf_t* ibuf, abuf_t* obuf, unsigned outChIdx )
{
// for each channel
for(unsigned i=0; i<ibuf->chN && outChIdx<obuf->chN; ++i)
{
sample_t* isig = ibuf->buf + i * ibuf->frameN;
sample_t* osig = obuf->buf + outChIdx * obuf->frameN;
sample_t gain = 1;
var_get(ctx,kGainPId,outChIdx,gain);
// apply the gain
for(unsigned j=0; j<ibuf->frameN; ++j)
osig[j] = gain * isig[j];
outChIdx += 1;
}
return outChIdx;
}
/*
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* ibuf0 = nullptr;
const abuf_t* ibuf1 = nullptr;
abuf_t* obuf = nullptr;
unsigned oChIdx = 0;
if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC )
goto errLabel;
if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC )
goto errLabel;
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
oChIdx = _exec( ctx, ibuf0, obuf, oChIdx );
oChIdx = _exec( ctx, ibuf1, obuf, oChIdx );
assert( oChIdx == obuf->chN );
errLabel:
return rc;
}
*/
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
abuf_t* obuf = nullptr;
unsigned oChIdx = 0;
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
for(unsigned i=0; i<inst->srcN; ++i)
{
const abuf_t* ibuf = nullptr;
if((rc = var_get(ctx,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
oChIdx = _exec( ctx, ibuf, obuf, oChIdx );
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_mix
//
namespace audio_mix
{
enum {
kIn0PId,
kIn1PId,
kGain0PId,
kGain1PId,
kOutPId,
};
typedef struct
{
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf0 = nullptr; //
const abuf_t* abuf1 = nullptr;
unsigned outChN = 0;
double dum;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,
kIn0PId,"in0",kBaseSfxId,abuf0,
kIn1PId,"in1",kBaseSfxId,abuf1 )) != kOkRC )
{
goto errLabel;
}
assert( abuf0->frameN == abuf1->frameN );
outChN = std::max(abuf0->chN, abuf1->chN);
// register the gain
var_register_and_get( ctx, kAnyChIdx, kGain0PId, "gain0", kBaseSfxId, dum );
var_register_and_get( ctx, kAnyChIdx, kGain1PId, "gain1", kBaseSfxId, dum );
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{ return kOkRC; }
rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; }
rc_t _mix( instance_t* ctx, unsigned inPId, unsigned gainPId, abuf_t* obuf )
{
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
if((rc = var_get(ctx, inPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
if(rc == kOkRC )
{
unsigned chN = std::min(ibuf->chN, obuf->chN );
for(unsigned i=0; i<chN; ++i)
{
const sample_t* isig = ibuf->buf + i*ibuf->frameN;
sample_t* osig = obuf->buf + i*obuf->frameN;
coeff_t gain = 1;
if((rc = var_get(ctx, gainPId, kAnyChIdx, gain)) != kOkRC )
goto errLabel;
for(unsigned j=0; j<obuf->frameN; ++j)
osig[j] += gain * isig[j];
}
}
errLabel:
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
abuf_t* obuf = nullptr;
//const abuf_t* ibuf0 = nullptr;
//const abuf_t* ibuf1 = nullptr;
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
//if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC )
// goto errLabel;
//if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC )
// goto errLabel;
vop::zero(obuf->buf, obuf->frameN*obuf->chN );
_mix( ctx, kIn0PId, kGain0PId, obuf );
_mix( ctx, kIn1PId, kGain1PId, obuf );
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// sine_tone
//
namespace sine_tone
{
enum
{
kSratePId,
kChCntPid,
kFreqHzPId,
kPhasePId,
kDcPId,
kGainPId,
kOutPId
};
typedef struct
{
double *phaseA;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = mem::allocZ<inst_t>();
srate_t srate = 0;
unsigned chCnt = 0;
coeff_t gain;
coeff_t hz;
coeff_t phase;
coeff_t dc;
ctx->userPtr = inst;
// Register variables and get their current value
if((rc = var_register_and_get( ctx, kAnyChIdx, kChCntPid, "chCnt", kBaseSfxId, chCnt)) != kOkRC )
{
goto errLabel;
}
// register each oscillator variable
for(unsigned i=0; i<chCnt; ++i)
if((rc = var_register_and_get( ctx, i,
kSratePId, "srate", kBaseSfxId, srate,
kFreqHzPId, "hz", kBaseSfxId, hz,
kPhasePId, "phase", kBaseSfxId, phase,
kDcPId, "dc", kBaseSfxId, dc,
kGainPId, "gain", kBaseSfxId, gain)) != kOkRC )
{
goto errLabel;
}
// create one output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, chCnt, ctx->ctx->framesPerCycle );
inst->phaseA = mem::allocZ<double>( chCnt );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst->phaseA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
abuf_t* abuf = nullptr;
// get the output signal buffer
if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC )
{
rc = cwLogError(kInvalidStateRC,"The Sine Tone instance '%s' does not have a valid audio output buffer.",ctx->label);
}
else
{
for(unsigned i=0; i<abuf->chN; ++i)
{
coeff_t gain = val_get<coeff_t>( ctx, kGainPId, i );
coeff_t hz = val_get<coeff_t>( ctx, kFreqHzPId, i );
coeff_t phase = val_get<coeff_t>( ctx, kPhasePId, i );
coeff_t dc = val_get<coeff_t>( ctx, kDcPId, i );
srate_t srate = val_get<srate_t>(ctx, kSratePId, i );
sample_t* v = abuf->buf + (i*abuf->frameN);
for(unsigned j=0; j<abuf->frameN; ++j)
v[j] = (sample_t)((gain * sin( inst->phaseA[i] + phase + (2.0 * M_PI * j * hz/srate)))+dc);
inst->phaseA[i] += 2.0 * M_PI * abuf->frameN * hz/srate;
}
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Phase Vocoder (Analysis)
//
namespace pv_analysis
{
typedef struct dsp::pv_anl::obj_str<sample_t,fd_sample_t> pv_t;
enum {
kInPId,
kMaxWndSmpNPId,
kWndSmpNPId,
kHopSmpNPId,
kHzFlPId,
kOutPId
};
typedef struct
{
pv_t** pvA; // pvA[ srcBuf.chN ]
unsigned pvN;
unsigned maxWndSmpN;
unsigned wndSmpN;
unsigned hopSmpN;
bool hzFl;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* srcBuf = nullptr; //
unsigned flags = 0;
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", kBaseSfxId, srcBuf )) != kOkRC )
{
cwLogError(kInvalidArgRC,"Unable to access the 'src' buffer.");
}
else
{
flags = inst->hzFl ? dsp::pv_anl::kCalcHzPvaFl : dsp::pv_anl::kNoCalcHzPvaFl;
inst->pvN = srcBuf->chN;
inst->pvA = mem::allocZ<pv_t*>( inst->pvN ); // allocate pv channel array
const fd_sample_t* magV[ srcBuf->chN ];
const fd_sample_t* phsV[ srcBuf->chN ];
const fd_sample_t* hzV[ srcBuf->chN ];
unsigned maxBinNV[ srcBuf->chN ];
unsigned binNV[ srcBuf->chN ];
unsigned hopNV[ srcBuf->chN ];
// create a pv anlaysis object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
unsigned maxWndSmpN = 0;
unsigned wndSmpN = 0;
unsigned hopSmpN = 0;
bool hzFl = false;
if((rc = var_register_and_get( ctx, i,
kMaxWndSmpNPId, "maxWndSmpN", kBaseSfxId, maxWndSmpN,
kWndSmpNPId, "wndSmpN", kBaseSfxId, wndSmpN,
kHopSmpNPId, "hopSmpN", kBaseSfxId, hopSmpN,
kHzFlPId, "hzFl", kBaseSfxId, hzFl )) != kOkRC )
{
goto errLabel;
}
if((rc = create( inst->pvA[i], ctx->ctx->framesPerCycle, srcBuf->srate, maxWndSmpN, wndSmpN, hopSmpN, flags )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The PV analysis object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
maxBinNV[i] = inst->pvA[i]->maxBinCnt;
binNV[i] = inst->pvA[i]->binCnt;
hopNV[i] = hopSmpN;
magV[i] = inst->pvA[i]->magV;
phsV[i] = inst->pvA[i]->phsV;
hzV[i] = inst->pvA[i]->hzV;
}
// create the fbuf 'out'
if((rc = var_register_and_set(ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, maxBinNV, binNV, hopNV, magV, phsV, hzV )) != kOkRC )
{
cwLogError(kOpFailRC,"The output freq. buffer could not be created.");
goto errLabel;
}
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->pvN; ++i)
destroy(inst->pvA[i]);
mem::release(inst->pvA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
if( var->chIdx != kAnyChIdx && var->chIdx < inst->pvN )
{
unsigned val = 0;
pv_t* pva = inst->pvA[ var->chIdx ];
switch( var->vid )
{
case kWndSmpNPId:
rc = var_get( var, val );
dsp::pv_anl::set_window_length(pva,val);
//printf("WL:%i %i\n",val,var->chIdx);
break;
}
}
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* srcBuf = nullptr;
fbuf_t* dstBuf = nullptr;
// verify that a source buffer exists
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
// verify that the dst buffer exits
if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid output.",ctx->label);
goto errLabel;
}
// for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
dstBuf->readyFlV[i] = false;
// call the PV analysis processor
if( dsp::pv_anl::exec( inst->pvA[i], srcBuf->buf + i*srcBuf->frameN, srcBuf->frameN ) )
{
// rescale the frequency domain magnitude
vop::mul(dstBuf->magV[i], dstBuf->binN_V[i]/2, dstBuf->binN_V[i]);
dstBuf->readyFlV[i] = true;
}
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Phase Vocoder (Synthesis)
//
namespace pv_synthesis
{
typedef struct dsp::pv_syn::obj_str<sample_t,fd_sample_t> pv_t;
enum {
kInPId,
kOutPId
};
typedef struct
{
pv_t** pvA; // pvA[ srcBuf.chN ]
unsigned pvN;
unsigned wndSmpN; //
unsigned hopSmpN; //
bool hzFl; //
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const fbuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", kBaseSfxId, srcBuf)) != kOkRC )
{
goto errLabel;
}
else
{
// allocate pv channel array
inst->pvN = srcBuf->chN;
inst->pvA = mem::allocZ<pv_t*>( inst->pvN );
// create a pv anlaysis object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
unsigned wndSmpN = (srcBuf->binN_V[i]-1)*2;
if((rc = create( inst->pvA[i], ctx->ctx->framesPerCycle, srcBuf->srate, wndSmpN, srcBuf->hopSmpN_V[i] )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The PV synthesis object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
}
if((rc = var_register( ctx, kAnyChIdx, kInPId, "in", kBaseSfxId)) != kOkRC )
goto errLabel;
// create the abuf 'out'
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, ctx->ctx->framesPerCycle );
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->pvN; ++i)
destroy(inst->pvA[i]);
mem::release(inst->pvA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const fbuf_t* srcBuf = nullptr;
abuf_t* dstBuf = nullptr;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC )
goto errLabel;
for(unsigned i=0; i<srcBuf->chN; ++i)
{
if( srcBuf->readyFlV[i] )
dsp::pv_syn::exec( inst->pvA[i], srcBuf->magV[i], srcBuf->phsV[i] );
const sample_t* ola_out = dsp::ola::execOut(inst->pvA[i]->ola);
if( ola_out != nullptr )
abuf_set_channel( dstBuf, i, ola_out, inst->pvA[i]->ola->procSmpCnt );
//abuf_set_channel( dstBuf, i, inst->pvA[i]->ola->outV, dstBuf->frameN );
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Spec Dist
//
namespace spec_dist
{
typedef struct dsp::spec_dist::obj_str<fd_sample_t,fd_sample_t> spec_dist_t;
enum
{
kInPId,
kBypassPId,
kCeilingPId,
kExpoPId,
kThreshPId,
kUprSlopePId,
kLwrSlopePId,
kMixPId,
kOutPId,
};
typedef struct
{
spec_dist_t** sdA;
unsigned sdN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const fbuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// verify that a source buffer exists
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
else
{
// allocate pv channel array
inst->sdN = srcBuf->chN;
inst->sdA = mem::allocZ<spec_dist_t*>( inst->sdN );
const fd_sample_t* magV[ srcBuf->chN ];
const fd_sample_t* phsV[ srcBuf->chN ];
const fd_sample_t* hzV[ srcBuf->chN ];
//if((rc = var_register(ctx, kAnyChIdx, kInPId, "in")) != kOkRC )
// goto errLabel;
// create a spec_dist object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
if((rc = create( inst->sdA[i], srcBuf->binN_V[i] )) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The 'spec dist' object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
// setup the output buffer pointers
magV[i] = inst->sdA[i]->outMagV;
phsV[i] = inst->sdA[i]->outPhsV;
hzV[i] = nullptr;
spec_dist_t* sd = inst->sdA[i];
if((rc = var_register_and_get( ctx, i,
kBypassPId, "bypass", kBaseSfxId, sd->bypassFl,
kCeilingPId, "ceiling", kBaseSfxId, sd->ceiling,
kExpoPId, "expo", kBaseSfxId, sd->expo,
kThreshPId, "thresh", kBaseSfxId, sd->thresh,
kUprSlopePId, "upr", kBaseSfxId, sd->uprSlope,
kLwrSlopePId, "lwr", kBaseSfxId, sd->lwrSlope,
kMixPId, "mix", kBaseSfxId, sd->mix )) != kOkRC )
{
goto errLabel;
}
}
// create the output buffer
if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->maxBinN_V, srcBuf->binN_V, srcBuf->hopSmpN_V, magV, phsV, hzV )) != kOkRC )
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->sdN; ++i)
destroy(inst->sdA[i]);
mem::release(inst->sdA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
if( var->chIdx != kAnyChIdx && var->chIdx < inst->sdN )
{
double val = 0;
spec_dist_t* sd = inst->sdA[ var->chIdx ];
switch( var->vid )
{
case kBypassPId: rc = var_get( var, val ); sd->bypassFl = val; break;
case kCeilingPId: rc = var_get( var, val ); sd->ceiling = val; break;
case kExpoPId: rc = var_get( var, val ); sd->expo = val; break;
case kThreshPId: rc = var_get( var, val ); sd->thresh = val; break;
case kUprSlopePId: rc = var_get( var, val ); sd->uprSlope = val; break;
case kLwrSlopePId: rc = var_get( var, val ); sd->lwrSlope = val; break;
case kMixPId: rc = var_get( var, val ); sd->mix = val; break;
default:
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label );
}
//printf("%i sd: ceil:%f expo:%f thresh:%f upr:%f lwr:%f mix:%f : rc:%i val:%f var:%s \n",
// var->chIdx,sd->ceiling, sd->expo, sd->thresh, sd->uprSlope, sd->lwrSlope, sd->mix, rc, val, var->label );
}
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const fbuf_t* srcBuf = nullptr;
fbuf_t* dstBuf = nullptr;
unsigned chN = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC )
goto errLabel;
chN = std::min(srcBuf->chN,inst->sdN);
for(unsigned i=0; i<chN; ++i)
{
dstBuf->readyFlV[i] = false;
if( srcBuf->readyFlV[i] )
{
dsp::spec_dist::exec( inst->sdA[i], srcBuf->magV[i], srcBuf->phsV[i], srcBuf->binN_V[i] );
dstBuf->readyFlV[i] = true;
//If == 0 )
// printf("%f %f\n", vop::sum(srcBuf->magV[i],srcBuf->binN), vop::sum(dstBuf->magV[i], dstBuf->binN) );
}
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Compressor
//
namespace compressor
{
enum
{
kInPId,
kBypassPId,
kInGainPId,
kThreshPId,
kRatioPId,
kAtkMsPId,
kRlsMsPId,
kWndMsPId,
kMaxWndMsPId,
kOutGainPId,
kOutPId,
kEnvPId
};
typedef dsp::compressor::obj_t compressor_t;
typedef struct
{
compressor_t** cmpA;
unsigned cmpN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// verify that a source buffer exists
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
else
{
// allocate pv channel array
inst->cmpN = srcBuf->chN;
inst->cmpA = mem::allocZ<compressor_t*>( inst->cmpN );
// create a compressor object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
coeff_t igain, thresh, ratio, ogain;
ftime_t maxWnd_ms, wnd_ms, atk_ms, rls_ms;
bool bypassFl;
// get the compressor variable values
if((rc = var_register_and_get( ctx, i,
kBypassPId, "bypass", kBaseSfxId, bypassFl,
kInGainPId, "igain", kBaseSfxId, igain,
kThreshPId, "thresh", kBaseSfxId, thresh,
kRatioPId, "ratio", kBaseSfxId, ratio,
kAtkMsPId, "atk_ms", kBaseSfxId, atk_ms,
kRlsMsPId, "rls_ms", kBaseSfxId, rls_ms,
kWndMsPId, "wnd_ms", kBaseSfxId, wnd_ms,
kMaxWndMsPId, "maxWnd_ms", kBaseSfxId, maxWnd_ms,
kOutGainPId, "ogain", kBaseSfxId, ogain )) != kOkRC )
{
goto errLabel;
}
// create the compressor instance
if((rc = dsp::compressor::create( inst->cmpA[i], srcBuf->srate, srcBuf->frameN, igain, maxWnd_ms, wnd_ms, thresh, ratio, atk_ms, rls_ms, ogain, bypassFl)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The 'compressor' object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
}
// create the output audio buffer
if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC )
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->cmpN; ++i)
destroy(inst->cmpA[i]);
mem::release(inst->cmpA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
ftime_t tmp;
if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN )
{
compressor_t* c = inst->cmpA[ var->chIdx ];
switch( var->vid )
{
case kBypassPId: rc = var_get( var, tmp ); c->bypassFl=tmp; break;
case kInGainPId: rc = var_get( var, tmp ); c->inGain=tmp; break;
case kOutGainPId: rc = var_get( var, tmp ); c->outGain=tmp; break;
case kRatioPId: rc = var_get( var, tmp ); c->ratio_num=tmp; break;
case kThreshPId: rc = var_get( var, tmp ); c->threshDb=tmp; break;
case kAtkMsPId: rc = var_get( var, tmp ); dsp::compressor::set_attack_ms(c, tmp ); break;
case kRlsMsPId: rc = var_get( var, tmp ); dsp::compressor::set_release_ms(c, tmp ); break;
case kWndMsPId: rc = var_get( var, tmp ); dsp::compressor::set_rms_wnd_ms(c, tmp ); break;
case kMaxWndMsPId: break;
default:
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label );
}
//printf("cmp byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n",
// c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp);
}
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* srcBuf = nullptr;
abuf_t* dstBuf = nullptr;
unsigned chN = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC )
goto errLabel;
chN = std::min(srcBuf->chN,inst->cmpN);
for(unsigned i=0; i<chN; ++i)
{
dsp::compressor::exec( inst->cmpA[i], srcBuf->buf + i*srcBuf->frameN, dstBuf->buf + i*srcBuf->frameN, srcBuf->frameN );
}
errLabel:
return rc;
}
rc_t report( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->cmpN; ++i)
{
compressor_t* c = inst->cmpA[i];
cwLogInfo("%s ch:%i : sr:%f bypass:%i procSmpN:%i igain:%f threshdb:%f ratio:%f atkSmp:%i rlsSmp:%i ogain:%f rmsWndN:%i maxRmsWndN%i",
ctx->label,i,c->srate,c->bypassFl,c->procSmpCnt,c->inGain,c->threshDb,c->ratio_num,c->atkSmp,c->rlsSmp,c->outGain,c->rmsWndCnt,c->rmsWndAllocCnt
);
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = report
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Limiter
//
namespace limiter
{
enum
{
kInPId,
kBypassPId,
kInGainPId,
kThreshPId,
kOutGainPId,
kOutPId,
};
typedef dsp::limiter::obj_t limiter_t;
typedef struct
{
limiter_t** limA;
unsigned limN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// verify that a source buffer exists
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
else
{
// allocate pv channel array
inst->limN = srcBuf->chN;
inst->limA = mem::allocZ<limiter_t*>( inst->limN );
// create a limiter object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
coeff_t igain, thresh, ogain;
bool bypassFl;
// get the limiter variable values
if((rc = var_register_and_get( ctx, i,
kBypassPId, "bypass", kBaseSfxId, bypassFl,
kInGainPId, "igain", kBaseSfxId, igain,
kThreshPId, "thresh", kBaseSfxId, thresh,
kOutGainPId, "ogain", kBaseSfxId, ogain )) != kOkRC )
{
goto errLabel;
}
// create the limiter instance
if((rc = dsp::limiter::create( inst->limA[i], srcBuf->srate, srcBuf->frameN, igain, thresh, ogain, bypassFl)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The 'limiter' object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
}
// create the output audio buffer
if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC )
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->limN; ++i)
destroy(inst->limA[i]);
mem::release(inst->limA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
coeff_t rtmp;
bool btmp;
if( var->chIdx != kAnyChIdx && var->chIdx < inst->limN )
{
limiter_t* c = inst->limA[ var->chIdx ];
switch( var->vid )
{
case kBypassPId: rc = var_get( var, btmp ); c->bypassFl=btmp; break;
case kInGainPId: rc = var_get( var, rtmp ); c->igain=rtmp; break;
case kOutGainPId: rc = var_get( var, rtmp ); c->ogain=rtmp; break;
case kThreshPId: rc = var_get( var, rtmp ); c->thresh=rtmp; break;
default:
cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label );
}
//printf("lim byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n",
// c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp);
}
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* srcBuf = nullptr;
abuf_t* dstBuf = nullptr;
unsigned chN = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC )
goto errLabel;
chN = std::min(srcBuf->chN,inst->limN);
for(unsigned i=0; i<chN; ++i)
{
dsp::limiter::exec( inst->limA[i], srcBuf->buf + i*srcBuf->frameN, dstBuf->buf + i*srcBuf->frameN, srcBuf->frameN );
}
errLabel:
return rc;
}
rc_t report( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->limN; ++i)
{
limiter_t* c = inst->limA[i];
cwLogInfo("%s ch:%i : bypass:%i procSmpN:%i igain:%f threshdb:%f ogain:%f",
ctx->label,i,c->bypassFl,c->procSmpCnt,c->igain,c->thresh,c->ogain );
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = report
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_delay
//
namespace audio_delay
{
enum
{
kInPId,
kMaxDelayMsPId,
kDelayMsPId,
kOutPId
};
typedef struct inst_str
{
abuf_t* delayBuf; // delayBuf->buf[ maxDelayFrameN ]
unsigned maxDelayFrameN; // length of the delay
unsigned* cntV; // cntV[ chN ] per channel delay
unsigned* idxV; // idxV[ chN ] per channel i/o idx
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ftime_t delayMs = 0;
ftime_t maxDelayMs = 0;
ctx->userPtr = inst;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC )
goto errLabel;
inst->cntV = mem::allocZ<unsigned>(abuf->chN);
inst->idxV = mem::allocZ<unsigned>(abuf->chN);
// register the gain
for(unsigned i=0; i<abuf->chN; ++i)
{
if((rc = var_register_and_get( ctx, i,
kMaxDelayMsPId, "maxDelayMs", kBaseSfxId, maxDelayMs,
kDelayMsPId, "delayMs", kBaseSfxId, delayMs)) != kOkRC )
{
goto errLabel;
}
if( delayMs > maxDelayMs )
{
cwLogWarning("'delayMs' (%i) is being reduced to 'maxDelayMs' (%i) on the delay instance:%s.",delayMs,maxDelayMs,ctx->label);
delayMs = maxDelayMs;
}
inst->maxDelayFrameN = std::max(inst->maxDelayFrameN, (unsigned)(fabs(maxDelayMs) * abuf->srate / 1000.0) );
inst->cntV[i] = (unsigned)(fabs(delayMs) * abuf->srate / 1000.0);
}
inst->delayBuf = abuf_create( abuf->srate, abuf->chN, inst->maxDelayFrameN );
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
inst_t* inst = (inst_t*)ctx->userPtr;
mem::release(inst->cntV);
mem::release(inst->idxV);
abuf_destroy(inst->delayBuf);
mem::release(inst);
return kOkRC;
}
rc_t _update_delay( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
abuf_t* ibuf = nullptr;
ftime_t delayMs = 0;
unsigned delayFrameN = 0;
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
if((rc = var_get( var, delayMs )) != kOkRC )
goto errLabel;
delayFrameN = (unsigned)(fabs(delayMs) * ibuf->srate / 1000.0);
if( delayFrameN > inst->maxDelayFrameN )
{
delayFrameN = inst->maxDelayFrameN;
cwLogWarning("The audio delay length is limited to %i milliseconds.", (int)((delayFrameN * 1000) / ibuf->srate));
}
vop::zero(inst->delayBuf->buf,inst->delayBuf->chN*inst->delayBuf->frameN);
for(unsigned i=0; i<ibuf->chN; ++i)
{
inst->cntV[i] = delayFrameN;
inst->idxV[i] = 0;
}
errLabel:
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
switch( var->vid )
{
case kDelayMsPId:
rc = _update_delay(ctx,var);
break;
}
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr;
abuf_t* dbuf = inst->delayBuf;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
// for each channel
for(unsigned i=0; i<ibuf->chN; ++i)
{
sample_t* isig = ibuf->buf + i*ibuf->frameN;
sample_t* osig = obuf->buf + i*obuf->frameN;
sample_t* dsig = dbuf->buf + i*dbuf->frameN;
unsigned di = inst->idxV[i];
// if the delay is set to zero samples
if( inst->cntV[i] == 0 )
memcpy(osig,isig,ibuf->frameN * sizeof(sample_t));
else
{
// otherwise the delay is non-zero positive sample count
for(unsigned j=0; j<ibuf->frameN; ++j)
{
osig[j] = dsig[di]; // read delay output
dsig[di] = isig[j]; // set delay input
di = (di+1) % inst->cntV[i]; // update the delay index
}
}
// store the delay index for the next cycle
inst->idxV[i] = di;
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// DC Filter
//
namespace dc_filter
{
enum
{
kInPId,
kBypassPId,
kGainPId,
kOutPId,
};
typedef dsp::dc_filter::obj_t dc_filter_t;
typedef struct
{
dc_filter_t** dcfA;
unsigned dcfN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// verify that a source buffer exists
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
else
{
// allocate channel array
inst->dcfN = srcBuf->chN;
inst->dcfA = mem::allocZ<dc_filter_t*>( inst->dcfN );
// create a dc_filter object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
coeff_t gain;
bool bypassFl;
// get the dc_filter variable values
if((rc = var_register_and_get( ctx, i,
kBypassPId, "bypass", kBaseSfxId, bypassFl,
kGainPId, "gain", kBaseSfxId, gain )) != kOkRC )
{
goto errLabel;
}
// create the dc_filter instance
if((rc = dsp::dc_filter::create( inst->dcfA[i], srcBuf->srate, srcBuf->frameN, gain, bypassFl)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The 'dc_filter' object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
}
// create the output audio buffer
if((rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC )
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->dcfN; ++i)
destroy(inst->dcfA[i]);
mem::release(inst->dcfA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* srcBuf = nullptr;
abuf_t* dstBuf = nullptr;
unsigned chN = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC )
goto errLabel;
chN = std::min(srcBuf->chN,inst->dcfN);
for(unsigned i=0; i<chN; ++i)
{
coeff_t gain = val_get<coeff_t>( ctx, kGainPId, i );
bool bypassFl = val_get<bool>( ctx, kBypassPId, i );
dsp::dc_filter::set( inst->dcfA[i], gain, bypassFl );
dsp::dc_filter::exec( inst->dcfA[i], srcBuf->buf + i*srcBuf->frameN, dstBuf->buf + i*srcBuf->frameN, srcBuf->frameN );
}
errLabel:
return rc;
}
rc_t report( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->dcfN; ++i)
{
dc_filter_t* c = inst->dcfA[i];
cwLogInfo("%s ch:%i : bypass:%i gain:%f",
ctx->label,i,c->bypassFl,c->gain );
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = report
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_meter
//
namespace audio_meter
{
enum
{
kInPId,
kDbFlPId,
kWndMsPId,
kPeakDbPId,
kOutPId,
kPeakFlPId,
kClipFlPId
};
typedef dsp::audio_meter::obj_t audio_meter_t;
typedef struct
{
audio_meter_t** mtrA;
unsigned mtrN;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* srcBuf = nullptr; //
inst_t* inst = mem::allocZ<inst_t>();
ctx->userPtr = inst;
// verify that a source buffer exists
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC )
{
rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label);
goto errLabel;
}
else
{
// allocate channel array
inst->mtrN = srcBuf->chN;
inst->mtrA = mem::allocZ<audio_meter_t*>( inst->mtrN );
// create a audio_meter object for each input channel
for(unsigned i=0; i<srcBuf->chN; ++i)
{
ftime_t wndMs;
coeff_t peakThreshDb;
bool dbFl;
// get the audio_meter variable values
if((rc = var_register_and_get( ctx, i,
kDbFlPId, "dbFl", kBaseSfxId, dbFl,
kWndMsPId, "wndMs", kBaseSfxId, wndMs,
kPeakDbPId, "peakDb", kBaseSfxId, peakThreshDb )) != kOkRC )
{
goto errLabel;
}
// get the audio_meter variable values
if((rc = var_register( ctx, i,
kOutPId, "out", kBaseSfxId,
kPeakFlPId, "peakFl", kBaseSfxId,
kClipFlPId, "clipFl", kBaseSfxId )) != kOkRC )
{
goto errLabel;
}
unsigned maxWndMs = std::max(wndMs,1000.0);
// create the audio_meter instance
if((rc = dsp::audio_meter::create( inst->mtrA[i], srcBuf->srate, maxWndMs, wndMs, peakThreshDb)) != kOkRC )
{
rc = cwLogError(kOpFailRC,"The 'audio_meter' object create failed on the instance '%s'.",ctx->label);
goto errLabel;
}
}
}
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->mtrN; ++i)
destroy(inst->mtrA[i]);
mem::release(inst->mtrA);
mem::release(inst);
return rc;
}
rc_t value( instance_t* ctx, variable_t* var )
{
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
const abuf_t* srcBuf = nullptr;
unsigned chN = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC )
goto errLabel;
chN = std::min(srcBuf->chN,inst->mtrN);
for(unsigned i=0; i<chN; ++i)
{
dsp::audio_meter::exec( inst->mtrA[i], srcBuf->buf + i*srcBuf->frameN, srcBuf->frameN );
var_set(ctx, kOutPId, i, inst->mtrA[i]->outDb );
var_set(ctx, kPeakFlPId, i, inst->mtrA[i]->peakFl );
var_set(ctx, kClipFlPId, i, inst->mtrA[i]->clipFl );
}
errLabel:
return rc;
}
rc_t report( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* inst = (inst_t*)ctx->userPtr;
for(unsigned i=0; i<inst->mtrN; ++i)
{
audio_meter_t* c = inst->mtrA[i];
cwLogInfo("%s ch:%i : %f %f db : pk:%i %i clip:%i %i ",
ctx->label,i,c->outLin,c->outDb,c->peakFl,c->peakCnt,c->clipFl,c->clipCnt );
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = report
};
}
//------------------------------------------------------------------------------------------------------------------
//
// audio_marker
//
namespace audio_marker
{
enum
{
kInPId,
kMarkPId,
kOutPId
};
typedef struct inst_str
{
sample_t mark;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; //
ctx->userPtr = mem::allocZ<inst_t>();
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",kBaseSfxId,abuf )) != kOkRC )
goto errLabel;
// register the marker input
if((rc = var_register_and_set( ctx, kAnyChIdx, kMarkPId, "mark", kBaseSfxId, 0.0f )) != kOkRC )
goto errLabel;
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN );
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
inst_t* inst = (inst_t*)(ctx->userPtr);
mem::release(inst);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{
return kOkRC;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
abuf_t* obuf = nullptr;
//inst_t* inst = (inst_t*)(ctx->userPtr);
sample_t mark = 1;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
// get the dst buffer
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
var_get(ctx,kMarkPId,kAnyChIdx,mark);
// for each channel
for(unsigned i=0; i<ibuf->chN; ++i)
{
sample_t* isig = ibuf->buf + i*ibuf->frameN;
sample_t* osig = obuf->buf + i*obuf->frameN;
// apply the marker
for(unsigned j=0; j<ibuf->frameN; ++j)
osig[j] = mark + isig[j];
}
var_set(ctx,kMarkPId,kAnyChIdx,0.0f);
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// xfade_ctl
//
namespace xfade_ctl
{
enum {
kNetLabelPId,
kNetLabelSfxPId,
kSrateRefPId,
kDurMsPId,
kTriggerPId,
kGainPId,
};
typedef struct
{
unsigned xfadeDurMs; // crossfade duration in milliseconds
instance_t* net_proc; // source 'poly' network
network_t net; // internal proxy network
unsigned poly_ch_cnt; // set to 2 (one for 'cur' poly-ch., one for 'next' poly-ch.)
unsigned net_proc_cnt; // count of proc's in a single poly-channel (net_proc->proc_arrayN/poly_cnt)
unsigned cur_poly_ch_idx; //
unsigned next_poly_ch_idx; //
float* target_gainA; // target_gainA[net_proc->poly_cnt]
float* cur_gainA; // cur_gainA[net_proc->poly_cnt]
double srate;
} inst_t;
void _trigger_xfade( inst_t* p )
{
// begin fading out the cur channel
p->target_gainA[ p->cur_poly_ch_idx ] = 0;
// the next poly-ch become the cur poly-ch
p->cur_poly_ch_idx = p->next_poly_ch_idx;
// the next poly-ch advances
p->next_poly_ch_idx = p->next_poly_ch_idx+1 >= p->poly_ch_cnt ? 0 : p->next_poly_ch_idx+1;
// j selects a block of 'net_proc_cnt' slots in the proxy network which will become the 'next' channel
unsigned j = p->next_poly_ch_idx * p->net_proc_cnt;
// set the [j:j+poly_proc_cnt] pointers in the proxy net to the actual proc instances in the source net
for(unsigned i=0; i<p->net_proc->internal_net->proc_arrayN; ++i)
if( p->net_proc->internal_net->proc_array[i]->label_sfx_id == p->next_poly_ch_idx )
{
assert( p->next_poly_ch_idx * p->net_proc_cnt <= j
&& j < p->next_poly_ch_idx * p->net_proc_cnt + p->net_proc_cnt
&& j < p->net.proc_arrayN );
p->net.proc_array[j++] = p->net_proc->internal_net->proc_array[i];
}
// begin fading in the new cur channel
p->target_gainA[ p->cur_poly_ch_idx ] = 1;
// if the next channel is not already at 0 send it in that direction
p->target_gainA[ p->next_poly_ch_idx ] = 0;
}
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const char* netLabel = nullptr;
unsigned netLabelSfxId = kBaseSfxId;
bool trigFl = false;
variable_t* gainVar = nullptr;
abuf_t* srateSrc = nullptr;
double dum_dbl;
inst_t* p = mem::allocZ<inst_t>();
ctx->userPtr = p;
p->poly_ch_cnt = 2;
if((rc = var_register_and_get(ctx,kAnyChIdx,
kNetLabelPId, "net", kBaseSfxId, netLabel,
kNetLabelSfxPId, "netSfxId", kBaseSfxId, netLabelSfxId,
kSrateRefPId, "srateSrc", kBaseSfxId, srateSrc,
kDurMsPId, "durMs", kBaseSfxId, p->xfadeDurMs,
kTriggerPId, "trigger", kBaseSfxId, trigFl,
kGainPId, "gain", kBaseSfxId, dum_dbl)) != kOkRC )
{
goto errLabel;
}
// locate the source poly-network for this xfad-ctl
if((rc = instance_find(*ctx->net,netLabel,netLabelSfxId,p->net_proc)) != kOkRC )
{
cwLogError(rc,"The xfade_ctl source network proc instance '%s:%i' was not found.",cwStringNullGuard(netLabel),netLabelSfxId);
goto errLabel;
}
if( p->net_proc->internal_net->poly_cnt < 3 )
{
cwLogError(rc,"The xfade_ctl source network must have at least 3 poly channels. %i < 3",p->net_proc->internal_net->poly_cnt);
goto errLabel;
}
// create the gain output variables - one output for each poly-channel
for(unsigned i=1; i<p->net_proc->internal_net->poly_cnt; ++i)
{
variable_t* dum;
if((rc = var_create(ctx, "gain", i, kGainPId+i, kAnyChIdx, nullptr, kInvalidTFl, dum )) != kOkRC )
{
cwLogError(rc,"'gain:%i' create failed.",i);
goto errLabel;
}
}
// count of proc's in one poly-ch of the poly network
p->net_proc_cnt = p->net_proc->internal_net->proc_arrayN / p->net_proc->internal_net->poly_cnt;
// create the proxy network
p->net.proc_arrayAllocN = p->net_proc_cnt * p->poly_ch_cnt;
p->net.proc_arrayN = p->net.proc_arrayAllocN;
p->net.proc_array = mem::allocZ<instance_t*>(p->net.proc_arrayAllocN);
p->target_gainA = mem::allocZ<float>(p->net_proc->internal_net->poly_cnt);
p->cur_gainA = mem::allocZ<float>(p->net_proc->internal_net->poly_cnt);
p->srate = srateSrc->srate;
// make the proxy network public - xfad_ctl now looks like the source network
// because it has the same proc instances
ctx->internal_net = &p->net;
// setup the channels such that the first active channel after _trigger_xfade()
// will be channel 0
p->cur_poly_ch_idx = 1;
p->next_poly_ch_idx = 2;
_trigger_xfade(p); // cur=2 nxt=0 initialize inst ptrs in range: p->net[0:net_proc_cnt]
_trigger_xfade(p); // cur=0 nxt=1 initialize inst ptrs in range: p->net[net_proc_cnt:2*net_proc_cnt]
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{
inst_t* p = (inst_t*)ctx->userPtr;
mem::release(p->net.proc_array);
mem::release(p->target_gainA);
mem::release(p->cur_gainA);
mem::release(ctx->userPtr);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; }
// return sign of expression as a float
float _signum( float v ) { return (0.0f < v) - (v < 0.0f); }
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)ctx->userPtr;
bool trigFl = false;
// check if a cross-fade has been triggered
if((rc = var_get(ctx,kTriggerPId,kAnyChIdx,trigFl)) == kOkRC )
{
_trigger_xfade(p);
var_set(ctx,kTriggerPId,kAnyChIdx,false);
}
// time in sample frames to complete a xfade
double xfade_dur_smp = p->xfadeDurMs * p->srate / 1000.0;
// fraction of a xfade which will be completed in on exec() cycle
float delta_gain_per_cycle = (float)(ctx->ctx->framesPerCycle / xfade_dur_smp);
// update the cross-fade gain outputs
for(unsigned i=0; i<p->net_proc->internal_net->poly_cnt; ++i)
{
p->cur_gainA[i] += _signum(p->target_gainA[i] - p->cur_gainA[i]) * delta_gain_per_cycle;
p->cur_gainA[i] = std::min(1.0f, std::max(0.0f, p->cur_gainA[i]));
var_set(ctx,kGainPId+i,kAnyChIdx,p->cur_gainA[i]);
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// poly_mixer
//
namespace poly_mixer
{
enum {
kOutGainPId,
kOutPId,
};
typedef struct
{
unsigned inBaseVId;
unsigned gainBaseVId;
} inst_t;
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
/*
const abuf_t* abuf0 = nullptr; //
const abuf_t* abuf1 = nullptr;
unsigned outChN = 0;
double dum;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,
kIn0PId,"in0",kBaseSfxId,abuf0,
kIn1PId,"in1",kBaseSfxId,abuf1 )) != kOkRC )
{
goto errLabel;
}
assert( abuf0->frameN == abuf1->frameN );
outChN = std::max(abuf0->chN, abuf1->chN);
// register the gain
var_register_and_get( ctx, kAnyChIdx, kGain0PId, "gain0", kBaseSfxId, dum );
var_register_and_get( ctx, kAnyChIdx, kGain1PId, "gain1", kBaseSfxId, dum );
// create the output audio buffer
rc = var_register_and_set( ctx, "out", kBaseSfxId, kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN );
*/
errLabel:
return rc;
}
rc_t destroy( instance_t* ctx )
{ return kOkRC; }
rc_t value( instance_t* ctx, variable_t* var )
{ return kOkRC; }
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
/*
abuf_t* obuf = nullptr;
//const abuf_t* ibuf0 = nullptr;
//const abuf_t* ibuf1 = nullptr;
if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel;
//if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC )
// goto errLabel;
//if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC )
// goto errLabel;
vop::zero(obuf->buf, obuf->frameN*obuf->chN );
_mix( ctx, kIn0PId, kGain0PId, obuf );
_mix( ctx, kIn1PId, kGain1PId, obuf );
*/
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// sample_hold
//
namespace sample_hold
{
enum
{
kInPId,
kPeriodMsPId,
kOutPId,
kMeanPId,
};
typedef struct inst_str
{
unsigned chN; // count of audio input channels and output sample variables.
unsigned bufAllocFrmN; // count of sample frames allocated in the sample buffer
unsigned periodFrmN; // count of sample frames in the sample period
unsigned ii; // next buf[][] frame index to receive an incoming audio sample
sample_t** buf; // buf[chN][bufSmpAllocN]
} inst_t;
unsigned _period_ms_to_smp( srate_t srate, unsigned framesPerCycle, double periodMs )
{
unsigned frmN = (unsigned)(srate * periodMs / 1000.0);
return std::max(framesPerCycle,frmN);
}
unsigned _period_ms_to_smp( srate_t srate, unsigned framesPerCycle, unsigned bufSmpAllocN, double periodMs )
{
unsigned frmN = _period_ms_to_smp(srate,framesPerCycle, periodMs );
// clip sample period to the max. buffer length.
return std::min(bufSmpAllocN,frmN);
}
sample_t _mean( inst_t* p, unsigned chIdx, unsigned oi, unsigned n0, unsigned n1 )
{
sample_t sum = 0;
for(unsigned i=0; i<n0; ++i)
sum += p->buf[chIdx][oi + i ];
for(unsigned i=0; i<n1; ++i)
sum += p->buf[chIdx][i];
return n0+n1==0 ? 0 : sum/(n0+n1);
}
void _destroy( inst_t* p )
{
for(unsigned i=0; i<p->chN; ++i)
mem::release(p->buf[i]);
mem::release(p->buf);
mem::release(p);
}
rc_t create( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; //
double periodMs = 0;
ctx->userPtr = mem::allocZ<inst_t>();
inst_t* p = (inst_t*)ctx->userPtr;
// get the source audio buffer
if((rc = var_register_and_get(ctx, kAnyChIdx,
kInPId, "in", kBaseSfxId, abuf,
kPeriodMsPId, "period_ms",kBaseSfxId, periodMs)) != kOkRC )
{
goto errLabel;
}
p->chN = abuf->chN;
p->bufAllocFrmN = _period_ms_to_smp( abuf->srate, ctx->ctx->framesPerCycle, periodMs );
p->periodFrmN = p->bufAllocFrmN;
p->buf = mem::allocZ<sample_t*>(abuf->chN);
for(unsigned i=0; i<abuf->chN; ++i)
{
p->buf[i] = mem::allocZ<sample_t>(p->bufAllocFrmN);
if((rc = var_register_and_set(ctx, i,
kOutPId, "out", kBaseSfxId, 0.0f,
kMeanPId, "mean", kBaseSfxId, 0.0f)) != kOkRC )
{
goto errLabel;
}
}
errLabel:
if(rc != kOkRC )
_destroy(p);
return rc;
}
rc_t destroy( instance_t* ctx )
{
inst_t* p = (inst_t*)(ctx->userPtr);
_destroy(p);
return kOkRC;
}
rc_t value( instance_t* ctx, variable_t* var )
{
rc_t rc = kOkRC;
switch( var->vid )
{
case kPeriodMsPId:
{
double periodMs;
const abuf_t* abuf;
inst_t* p = (inst_t*)(ctx->userPtr);
var_get(ctx,kInPId,kAnyChIdx,abuf);
if((rc = var_get(var,periodMs)) == kOkRC )
{
p->periodFrmN = _period_ms_to_smp( abuf->srate, ctx->ctx->framesPerCycle, p->bufAllocFrmN, periodMs );
}
}
break;
default:
break;
}
return rc;
}
rc_t exec( instance_t* ctx )
{
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
inst_t* p = (inst_t*)(ctx->userPtr);
unsigned chN = 0;
unsigned oi = 0;
unsigned n0 = 0;
unsigned n1 = 0;
// get the src buffer
if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
chN = std::min(ibuf->chN,p->chN);
// Copy samples into buf.
for(unsigned i=0; i<ibuf->chN; ++i)
{
sample_t* isig = ibuf->buf + i*ibuf->frameN;
sample_t* obuf = p->buf[i];
unsigned k = p->ii;
for(unsigned j=0; j<ibuf->frameN; ++j)
{
obuf[k++] = isig[j];
if( k>= p->bufAllocFrmN )
k -= p->bufAllocFrmN;
}
}
// advance the input index
p->ii += ibuf->frameN;
if( p->ii >= p->bufAllocFrmN )
p->ii -= p->bufAllocFrmN;
// if the sampling buf is in range oi:ii
if( p->ii >= p->periodFrmN )
{
oi = p->ii - p->periodFrmN;
n0 = p->ii - oi;
n1 = 0;
}
else // the sampling buf is in two parts: bufAllocN-ii:bufAllocN, 0:ii
{
oi = p->bufAllocFrmN - (p->periodFrmN - p->ii);
n0 = p->bufAllocFrmN - oi;
n1 = p->ii;
}
for(unsigned i=0; i<ibuf->chN; ++i)
{
// the output is the first sample in the buffer
var_set(ctx,kOutPId,i, p->buf[i][oi] );
if( var_is_a_source(ctx,kMeanPId,i) )
var_set(ctx,kMeanPId,i, _mean(p,i,oi,n0,n1));
}
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Number
//
namespace number
{
enum {
kValuePId,
};
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
double value = 0;
if((rc = var_register_and_get(proc,kAnyChIdx,
kValuePId,"value",kBaseSfxId,value)) != kOkRC )
{
goto errLabel;
}
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* proc )
{
rc_t rc = kOkRC;
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Timer
//
namespace timer
{
enum {
kSratePId,
kPeriodMsPId,
kOutPId,
};
typedef struct
{
unsigned periodFrmN;
unsigned periodPhase;
} inst_t;
unsigned _period_ms_to_frame_count( instance_t* proc, inst_t* p, srate_t srate, ftime_t periodMs )
{
return std::max((unsigned)(srate * periodMs / 1000.0), proc->ctx->framesPerCycle);
}
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
ftime_t periodMs = 0;
srate_t srate = 0;
inst_t* p = mem::allocZ<inst_t>();
proc->userPtr = p;
if((rc = var_register_and_get(proc,kAnyChIdx,
kSratePId, "srate", kBaseSfxId,srate,
kPeriodMsPId, "period_ms",kBaseSfxId,periodMs)) != kOkRC )
{
goto errLabel;
}
if((rc = var_register_and_set(proc,kAnyChIdx,
kOutPId, "out", kBaseSfxId,false)) != kOkRC )
{
goto errLabel;
}
p->periodFrmN = _period_ms_to_frame_count(proc,p,srate,periodMs);
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
mem::release(p);
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
switch( var->vid )
{
case kPeriodMsPId:
{
double periodMs;
srate_t srate;
inst_t* p = (inst_t*)(proc->userPtr);
var_get(proc,kSratePId,kAnyChIdx,srate);
if((rc = var_get(var,periodMs)) == kOkRC )
p->periodFrmN = _period_ms_to_frame_count( proc, p, srate, periodMs );
}
break;
default:
break;
}
return rc;
}
rc_t exec( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
p->periodPhase += proc->ctx->framesPerCycle;
//printf("%i %i\n",p->periodPhase,p->periodFrmN);
if( p->periodPhase >= p->periodFrmN )
{
p->periodPhase -= p->periodFrmN;
bool val = false;
var_get(proc,kOutPId,kAnyChIdx,val);
//printf("%i %i %i\n",p->periodPhase,p->periodFrmN,val);
var_set(proc,kOutPId,kAnyChIdx,!val);
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// Counter
//
namespace counter
{
enum {
kTriggerPId,
kResetPId,
kInitPId,
kMinPId,
kMaxPId,
kIncPId,
kRepeatPId,
kModePId,
kOutTypePId,
kOutPId
};
enum {
kModuloModeId,
kReverseModeId,
kClipModeId,
kInvalidModeId
};
typedef struct
{
unsigned mode_id;
bool trig_val;
bool delta_fl;
bool reset_val;
bool reset_fl;
bool done_fl;
double dir;
} inst_t;
idLabelPair_t modeArray[] = {
{ kModuloModeId, "modulo" },
{ kReverseModeId, "reverse" },
{ kClipModeId, "clip" },
{ kInvalidId, "<invalid>"}
};
unsigned _string_to_mode_id( const char* mode_label, unsigned& mode_id_ref )
{
rc_t rc = kOkRC;
mode_id_ref = kInvalidId;
for(unsigned i=0; modeArray[i].id != kInvalidId; ++i)
if( textIsEqual(modeArray[i].label,mode_label) )
{
mode_id_ref = modeArray[i].id;
return kOkRC;
}
return cwLogError(kInvalidArgRC,"'%s' is not a valid counter 'mode'.",cwStringNullGuard(mode_label));
}
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = mem::allocZ<inst_t>();
proc->userPtr = p;
double init_val;
const char* mode_label;
variable_t* dum = nullptr;
const char* out_type_label;
unsigned out_type_fl;
if((rc = var_register_and_get(proc, kAnyChIdx,
kTriggerPId, "trigger", kBaseSfxId, p->trig_val,
kResetPId, "reset", kBaseSfxId, p->reset_val,
kInitPId, "init", kBaseSfxId, init_val,
kModePId, "mode", kBaseSfxId, mode_label,
kOutTypePId, "out_type",kBaseSfxId, out_type_label)) != kOkRC )
{
goto errLabel;
}
if((rc = var_register(proc, kAnyChIdx,
kMinPId, "min", kBaseSfxId,
kMaxPId, "max", kBaseSfxId,
kIncPId, "inc", kBaseSfxId,
kRepeatPId, "repeat_fl", kBaseSfxId)) != kOkRC )
{
goto errLabel;
}
// get the type of the output
if(out_type_label==nullptr || (out_type_fl = value_type_label_to_flag( out_type_label )) == kInvalidTFl )
{
rc = cwLogError(kInvalidArgRC,"The output type '%s' is not a valid type.",cwStringNullGuard(out_type_label));
goto errLabel;
}
if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_fl, dum )) != kOkRC )
{
goto errLabel;
}
if((rc = var_set( proc, kOutPId, kAnyChIdx, 0u )) != kOkRC )
{
rc = cwLogError(rc,"Unable to set the initial counter value to %f.",init_val);
goto errLabel;
}
if((rc = _string_to_mode_id(mode_label,p->mode_id)) != kOkRC )
goto errLabel;
p->dir = 1.0;
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
mem::release(p);
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
switch( var->vid )
{
case kTriggerPId:
{
bool v;
if((rc = var_get(var,v)) == kOkRC )
{
if( !p->delta_fl )
p->delta_fl = p->trig_val != v;
p->trig_val = v;
}
}
break;
case kModePId:
{
const char* s;
if((rc = var_get(var,s)) == kOkRC )
rc = _string_to_mode_id(s,p->mode_id);
}
break;
}
return rc;
}
rc_t exec( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
double cnt,inc,minv,maxv;
bool v;
if( !p->delta_fl )
return rc;
p->delta_fl = false;
if((rc = var_get(proc,kTriggerPId,kAnyChIdx,v)) != kOkRC )
{
cwLogError(rc,"Fail!");
goto errLabel;
}
p->trig_val = v;
var_get(proc,kOutPId,kAnyChIdx,cnt);
var_get(proc,kIncPId,kAnyChIdx,inc);
var_get(proc,kMinPId,kAnyChIdx,minv);
var_get(proc,kMaxPId,kAnyChIdx,maxv);
cnt += p->dir * inc;
if( minv > cnt || cnt > maxv )
{
bool repeat_fl;
var_get(proc,kRepeatPId,kAnyChIdx,repeat_fl);
if( !repeat_fl )
p->done_fl = true;
else
{
if( cnt > maxv)
{
switch( p->mode_id )
{
case kModuloModeId:
while(cnt > maxv )
cnt = minv + (cnt-maxv);
break;
case kReverseModeId:
p->dir = -1 * p->dir;
while( cnt > maxv )
cnt = maxv - (cnt-maxv);
break;
case kClipModeId:
cnt = maxv;
break;
default:
assert(0);
}
}
if( cnt < minv)
{
switch( p->mode_id )
{
case kModuloModeId:
while( cnt < minv )
cnt = maxv - (minv-cnt);
break;
case kReverseModeId:
p->dir = -1 * p->dir;
while(cnt < minv )
cnt = minv + (minv-cnt);
break;
case kClipModeId:
cnt = minv;
break;
default:
assert(0);
}
}
}
}
// if the counter has not reached it's terminal state
if( !p->done_fl )
var_set(proc,kOutPId,kAnyChIdx,cnt);
errLabel:
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// List
//
namespace list
{
enum
{
kInPId,
kListPId,
kOutPId
};
typedef struct
{
unsigned listN; // the length of the list
const object_t* list; // the list
unsigned typeFl; // the output type
unsigned index; // the last index referenced
bool deltaFl;
} inst_t;
rc_t _determine_type( const object_t* list, unsigned& typeFl_ref )
{
rc_t rc = kOkRC;
typeFl_ref = kInvalidTFl;
enum { bool_idx, uint_idx, int_idx, float_idx, double_idx, string_idx, cfg_idx, typeN };
typedef struct type_map_str
{
unsigned idx;
unsigned typeFl;
unsigned cnt;
} type_map_t;
type_map_t typeA[] = {
{ bool_idx, kBoolTFl, 0 },
{ uint_idx, kUIntTFl, 0 },
{ int_idx, kIntTFl, 0 },
{ float_idx, kFloatTFl, 0 },
{ double_idx, kDoubleTFl, 0 },
{ string_idx, kStringTFl, 0 },
{ cfg_idx, kCfgTFl, 0 },
};
// count the number of each type of element in the list.
for(unsigned i=0; i<list->child_count(); ++i)
{
const object_t* c = list->child_ele(i);
switch( c->type->id )
{
case kCharTId: typeA[uint_idx].cnt+=1; break;
case kInt8TId: typeA[int_idx].cnt +=1; break;
case kUInt8TId: typeA[uint_idx].cnt+=1; break;
case kInt16TId: typeA[int_idx].cnt +=1; break;
case kUInt16TId: typeA[uint_idx].cnt+=1; break;
case kInt32TId: typeA[int_idx].cnt +=1; break;
case kUInt32TId: typeA[uint_idx].cnt+=1; break;
case kFloatTId: typeA[float_idx].cnt+=1; break;
case kDoubleTId: typeA[double_idx].cnt+=1; break;
case kBoolTId: typeA[bool_idx].cnt+=1; break;
case kStringTId: typeA[string_idx].cnt+=1; break;
case kCStringTId:typeA[string_idx].cnt+=1; break;
break;
default:
switch( c->type->id )
{
case kVectTId:
case kPairTId:
case kListTId:
case kDictTId:
typeA[cfg_idx].cnt +=1;
break;
default:
rc = cwLogError(kSyntaxErrorRC,"The object type '0x%x' is not a valid list entry type. %i",c->type->flags,list->child_count());
goto errLabel;
}
}
unsigned type_flag = kInvalidTFl; // type flag of one of the reference types
unsigned type_cnt = 0; // count of types
for(unsigned i=0; i<typeN; ++i)
if( typeA[i].cnt > 0 )
{
type_cnt += 1;
type_flag = typeA[i].typeFl;
}
// it is an error if more than one type of element was included in the list -
// and one of those types was string or cfg - having multiple numeric types
// is ok because they can be converted between each other - but string, and cfg's
// cannot be converted to numbers, nor can the be converted between each other.
if( type_cnt > 1 && (typeA[string_idx].cnt>0 || typeA[cfg_idx].cnt>0) )
{
rc = cwLogError(kInvalidArgRC,"The list types. The list must be all numerics, all strings, or all cfg. types.");
for(unsigned i=0; i<typeN; ++i)
if( typeA[i].cnt > 0 )
cwLogInfo("%i %s",typeA[i].cnt, value_type_flag_to_label(typeA[i].typeFl));
goto errLabel;
}
typeFl_ref = type_flag;
}
errLabel:
return rc;
}
template< typename T >
rc_t _set_out_tmpl( instance_t* proc, inst_t* p, unsigned idx, T& v )
{
rc_t rc;
const object_t* ele;
// get the list element to output
if((ele = p->list->child_ele(idx)) == nullptr )
{
rc = cwLogError(kEleNotFoundRC,"The list element at index %i could not be accessed.",idx);
goto errLabel;
}
// get the value of the list element
if((rc = ele->value(v)) != kOkRC )
{
rc = cwLogError(rc,"List value access failed on index %i",idx);
goto errLabel;
}
// set the output
if((rc = var_set(proc,kOutPId,kAnyChIdx,v)) != kOkRC )
{
rc = cwLogError(rc,"List output failed on index %i",idx);
goto errLabel;
}
errLabel:
return rc;
}
rc_t _set_output( instance_t* proc, inst_t* p )
{
rc_t rc;
unsigned idx;
if((rc = var_get(proc,kInPId,kAnyChIdx,idx)) != kOkRC )
{
rc = cwLogError(rc,"Unable to get the list index.");
goto errLabel;
}
// if the index has not changed then there is nothing to do
if( idx == p->index )
goto errLabel;
switch( p->typeFl )
{
case kUIntTFl:
{
unsigned v;
rc = _set_out_tmpl(proc,p,idx,v);
}
break;
case kIntTFl:
{
int v;
rc = _set_out_tmpl(proc,p,idx,v);
}
break;
case kFloatTFl:
{
float v;
rc = _set_out_tmpl(proc,p,idx,v);
}
break;
case kDoubleTFl:
{
double v;
rc = _set_out_tmpl(proc,p,idx,v);
}
break;
case kStringTFl:
{
const char* v;
rc = _set_out_tmpl(proc,p,idx,v);
}
break;
case kCfgTFl:
{
const object_t* v;
rc = _set_out_tmpl(proc,p,idx,v);
}
break;
default:
rc = cwLogError(kInvalidArgRC,"The list type flag %s (0x%x) is not valid.",value_type_flag_to_label(p->typeFl),p->typeFl);
break;
}
if(rc != kOkRC )
goto errLabel;
p->index = idx;
errLabel:
return rc;
}
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = mem::allocZ<inst_t>();
unsigned index;
proc->userPtr = p;
variable_t* dum = nullptr;
p->index = kInvalidIdx;
p->typeFl = kInvalidTFl;
p->deltaFl = false;
if((rc = var_register_and_get(proc, kAnyChIdx,
kInPId, "in", kBaseSfxId, index,
kListPId,"list", kBaseSfxId, p->list)) != kOkRC )
{
goto errLabel;
}
if( !p->list->is_list() )
{
cwLogError(kSyntaxErrorRC,"The list cfg. value is not a list.");
goto errLabel;
}
p->listN = p->list->child_count();
// determine what type of element is in the list
// (all elements in the this list must be of the same type: numeric,string,cfg)
if((rc = _determine_type( p->list, p->typeFl )) != kOkRC )
goto errLabel;
// create the output variable
if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, p->typeFl, dum )) != kOkRC )
{
rc = cwLogError(rc,"'out' var create failed.");
goto errLabel;
}
// set the initial value of the output
if((rc = _set_output(proc,p)) != kOkRC )
goto errLabel;
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
mem::release(p);
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
if( var->vid == kInPId )
{
inst_t* p = (inst_t*)proc->userPtr;
unsigned idx;
if( var_get(var,idx) == kOkRC && idx != p->index)
p->deltaFl = true;
}
return rc;
}
rc_t exec( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
if( p->deltaFl )
{
rc = _set_output(proc, p );
p->deltaFl = false;
}
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// add
//
namespace add
{
enum {
kOutPId,
kOTypePId,
kInPId
};
typedef struct
{
bool delta_fl;
unsigned inN;
} inst_t;
template< typename T >
rc_t _sum( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
T sum = 0;
// read and sum the inputs
for(unsigned i=0; i<p->inN; ++i)
{
T val;
if((rc = var_get(proc,kInPId+i,kAnyChIdx,val)) == kOkRC )
sum += val;
else
{
rc = cwLogError(rc,"Operand index %i read failed.",i);
goto errLabel;
}
}
// set the output
if((rc = var_set(var,sum)) != kOkRC )
{
rc = cwLogError(rc,"Result set failed.");
goto errLabel;
}
errLabel:
return rc;
}
rc_t _exec( instance_t* proc, variable_t* out_var=nullptr )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)(proc->userPtr);
if( !p->delta_fl )
return rc;
p->delta_fl = false;
if( out_var == nullptr )
if((rc = var_find(proc,kOutPId,kAnyChIdx,out_var)) != kOkRC )
{
rc = cwLogError(rc,"The output variable could not be found.");
goto errLabel;
}
switch( out_var->varDesc->type )
{
case kBoolTFl: rc = _sum<bool>(proc,out_var); break;
case kUIntTFl: rc = _sum<unsigned>(proc,out_var); break;
case kIntTFl: rc = _sum<int>(proc,out_var); break;
case kFloatTFl: rc = _sum<float>(proc,out_var); break;
case kDoubleTFl: rc = _sum<double>(proc,out_var); break;
default:
rc = cwLogError(kInvalidArgRC,"The output type %s (0x%x) is not valid.",value_type_flag_to_label(out_var->value->tflag),out_var->value->tflag);
goto errLabel;
}
if(rc != kOkRC )
rc = cwLogError(kOpFailRC,"Sum failed.");
errLabel:
return rc;
}
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = mem::allocZ<inst_t>();
proc->userPtr = p;
variable_t* out_var = nullptr;
const char* out_type_label = nullptr;
unsigned out_type_flag = kInvalidTFl;
unsigned sfxIdAllocN = instance_var_count(proc);
unsigned sfxIdA[ sfxIdAllocN ];
p->inN = 0;
// get a count of the number of input variables
if((rc = var_mult_sfx_id_array(proc, "in", sfxIdA, sfxIdAllocN, p->inN )) != kOkRC )
{
rc = cwLogError(rc,"Unable to obtain the array of mult label-sfx-id's for the variable 'in'.");
goto errLabel;
}
// if the adder has no inputs
if( p->inN == 0 )
{
rc = cwLogError(rc,"The 'add' unit '%s' appears to not have any inputs.",cwStringNullGuard(proc->label));
goto errLabel;
}
// sort the input id's in ascending order
std::sort(sfxIdA, sfxIdA + p->inN, [](unsigned& a,unsigned& b){ return a<b; } );
// register each of the input vars
for(unsigned i=0; i<p->inN; ++i)
{
variable_t* dum;
if((rc = var_register(proc, "in", sfxIdA[i], kInPId+i, kAnyChIdx, nullptr, dum )) != kOkRC )
{
rc = cwLogError(rc,"Variable registration failed for the variable 'in:%i'.",sfxIdA[i]);;
goto errLabel;
}
}
// Get the output type label as a string
if((rc = var_register_and_get(proc,kAnyChIdx,kOTypePId,"otype",kBaseSfxId,out_type_label)) != kOkRC )
{
rc = cwLogError(rc,"Variable registration failed for the variable 'otype:0'.");;
goto errLabel;
}
// Convert the output type label into a flag
if((out_type_flag = value_type_label_to_flag(out_type_label)) == kInvalidTFl )
{
rc = cwLogError(rc,"The type label '%s' does not identify a valid type.",cwStringNullGuard(out_type_label));;
goto errLabel;
}
// Create the output var
if((rc = var_create( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, nullptr, out_type_flag, out_var )) != kOkRC )
{
rc = cwLogError(rc,"The output variable create failed.");
goto errLabel;
}
/*
if((rc = var_set(proc,kOutPId,kAnyChIdx,0.0)) != kOkRC )
{
rc = cwLogError(rc,"Initial output variable set failed.");
goto errLabel;
}
*/
p->delta_fl=true;
_exec(proc,out_var);
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
mem::release(p);
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)(proc->userPtr);
if( kInPId <= var->vid && var->vid < kInPId+p->inN )
p->delta_fl = true;
return rc;
}
rc_t exec( instance_t* proc )
{
return _exec(proc);
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
//------------------------------------------------------------------------------------------------------------------
//
// preset
//
namespace preset
{
enum { kInPId };
enum { kPresetLabelCharN=255 };
typedef struct
{
char preset_label[ kPresetLabelCharN+1];
} inst_t;
rc_t _set_preset( instance_t* proc, inst_t* p, const char* preset_label )
{
rc_t rc = kOkRC;
if( preset_label == nullptr )
{
if((rc = var_get(proc, kInPId, kAnyChIdx, preset_label)) != kOkRC )
{
rc = cwLogError(rc,"The variable 'in read failed.");
goto errLabel;
}
}
if( preset_label == nullptr )
{
rc = cwLogError(kInvalidArgRC,"Preset application failed due to blank preset label.");
goto errLabel;
}
if((rc = network_apply_preset(*proc->net, preset_label)) != kOkRC )
{
rc = cwLogError(rc,"Appy preset '%s' failed.",cwStringNullGuard(preset_label));
goto errLabel;
}
if( textLength(preset_label) >= kPresetLabelCharN )
strncpy(p->preset_label,preset_label,kPresetLabelCharN);
else
{
rc = cwLogError(kBufTooSmallRC,"The preset label '%s' is to long.",cwStringNullGuard(preset_label));
goto errLabel;
}
errLabel:
return rc;
}
rc_t create( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = mem::allocZ<inst_t>();
proc->userPtr = p;
const char* label = nullptr;
p->preset_label[0] = 0;
if((rc = var_register_and_get(proc,kAnyChIdx,kInPId,"in",kBaseSfxId,label)) != kOkRC )
goto errLabel;
errLabel:
return rc;
}
rc_t destroy( instance_t* proc )
{
rc_t rc = kOkRC;
inst_t* p = (inst_t*)proc->userPtr;
// Custom clean-up code goes here
mem::release(p);
return rc;
}
rc_t value( instance_t* proc, variable_t* var )
{
rc_t rc = kOkRC;
return rc;
}
rc_t exec( instance_t* proc )
{
rc_t rc = kOkRC;
//inst_t* p = (inst_t*)proc->userPtr;
return rc;
}
class_members_t members = {
.create = create,
.destroy = destroy,
.value = value,
.exec = exec,
.report = nullptr
};
}
} // flow
} // cw