6638 linhas
201 KiB
C
6638 linhas
201 KiB
C
//( { file_desc:"'snap' audio effects processor units." kw:[snap]}
|
|
|
|
#include "cmPrefix.h"
|
|
#include "cmGlobal.h"
|
|
#include "cmFloatTypes.h"
|
|
#include "cmComplexTypes.h"
|
|
#include "cmRpt.h"
|
|
#include "cmErr.h"
|
|
#include "cmCtx.h"
|
|
#include "cmMem.h"
|
|
#include "cmMallocDebug.h"
|
|
#include "cmLinkedHeap.h"
|
|
#include "cmText.h"
|
|
#include "cmMath.h"
|
|
#include "cmFile.h"
|
|
#include "cmFileSys.h"
|
|
#include "cmSymTbl.h"
|
|
#include "cmJson.h"
|
|
#include "cmPrefs.h"
|
|
#include "cmDspValue.h"
|
|
#include "cmMsgProtocol.h"
|
|
#include "cmThread.h"
|
|
#include "cmUdpPort.h"
|
|
#include "cmUdpNet.h"
|
|
#include "cmTime.h"
|
|
#include "cmAudioSys.h"
|
|
#include "cmProcObj.h"
|
|
#include "cmDspCtx.h"
|
|
#include "cmDspClass.h"
|
|
#include "cmDspUi.h"
|
|
#include "cmAudioFile.h"
|
|
|
|
#include "cmProcObj.h"
|
|
#include "cmProcTemplateMain.h"
|
|
#include "cmProc.h"
|
|
#include "cmMidi.h"
|
|
#include "cmProc2.h"
|
|
#include "cmProc3.h"
|
|
#include "cmVectOpsTemplateMain.h"
|
|
|
|
#include "app/cmPickup.h"
|
|
|
|
#include "cmDspSys.h"
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspDelay file_desc:"Simple audio delay with feedback." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassDyId,
|
|
kTimeDyId,
|
|
kFbDyId,
|
|
kInDyId,
|
|
kOutDyId
|
|
|
|
};
|
|
|
|
cmDspClass_t _cmDelayDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmMDelay* delay;
|
|
} cmDspDelay_t;
|
|
|
|
cmDspInst_t* _cmDspDelayAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
unsigned chs = 1;
|
|
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "bypass",kBypassDyId,0,0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Bypass enable flag." },
|
|
{ "time", kTimeDyId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Max delay time in milliseconds" },
|
|
{ "fb", kFbDyId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Feedback" },
|
|
{ "in", kInDyId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "out", kOutDyId, 0, chs, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
|
|
{ NULL, 0, 0, 0, 0 }
|
|
};
|
|
|
|
cmDspDelay_t* p = cmDspInstAlloc(cmDspDelay_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassDyId,0, 0 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kTimeDyId, 0, 1000 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst ,kFbDyId, 0.0, 0.0 );
|
|
|
|
cmReal_t dtimeMs = cmDspDefaultUInt(&p->inst,kTimeDyId);
|
|
cmReal_t fbCoeff = cmDspDefaultDouble(&p->inst,kFbDyId);
|
|
|
|
p->delay = cmMDelayAlloc(ctx->cmProcCtx,NULL, cmDspSamplesPerCycle(ctx), cmDspSampleRate(ctx), fbCoeff, 1, &dtimeMs, NULL );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDelayFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspDelay_t* p = (cmDspDelay_t*)inst;
|
|
|
|
cmMDelayFree(&p->delay);
|
|
|
|
//cmCtxFree(&p->ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDelayReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDelayExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspDelay_t* p = (cmDspDelay_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInDyId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInDyId);
|
|
|
|
unsigned oChIdx = 0;
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutDyId,oChIdx);
|
|
unsigned oSmpCnt = cmDspVarRows(inst,kOutDyId);
|
|
|
|
bool bypassFl= cmDspBool(inst,kBypassDyId);
|
|
|
|
cmMDelayExec(p->delay,ip,op,cmMin(iSmpCnt,oSmpCnt),bypassFl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDelayRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspDelay_t* p = (cmDspDelay_t*)inst;
|
|
cmDspRC_t rc= kOkDspRC;
|
|
|
|
cmDspSetEvent(ctx,inst,evt);
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kBypassDyId:
|
|
break;
|
|
|
|
case kTimeDyId:
|
|
p->delay->delayArray[0].delayMs = cmDspDouble(inst,kTimeDyId);
|
|
break;
|
|
|
|
case kFbDyId:
|
|
p->delay->fbCoeff = cmDspDouble(inst,kFbDyId);
|
|
break;
|
|
|
|
default:
|
|
{ assert(0); }
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmDelayClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmDelayDC,ctx,"Delay",
|
|
NULL,
|
|
_cmDspDelayAlloc,
|
|
_cmDspDelayFree,
|
|
_cmDspDelayReset,
|
|
_cmDspDelayExec,
|
|
_cmDspDelayRecv,
|
|
NULL,NULL,
|
|
"Simple delay.");
|
|
|
|
return &_cmDelayDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspMtDelay file_desc:"Multi-tap audio delay with feedback." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassMtId,
|
|
kScaleMtId,
|
|
kFbMtId,
|
|
kInMtId,
|
|
kOutMtId,
|
|
kBaseMsMtId
|
|
|
|
};
|
|
|
|
cmDspClass_t _cmMtDelayDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmMDelay* p;
|
|
unsigned baseGainMtId;
|
|
unsigned tapCnt;
|
|
cmReal_t* msV;
|
|
cmReal_t* gainV;
|
|
unsigned printSymId;
|
|
} cmDspMtDelay_t;
|
|
|
|
// args: bypassFl, time_scale, feedback, tap_ms0, tap_gain0, tapms1, tap_gain1, ....
|
|
|
|
cmDspInst_t* _cmDspMtDelayAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
va_list vl1;
|
|
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "bypass",kBypassMtId,0, 0, kInDsvFl | kBoolDsvFl | kReqArgDsvFl, "Bypass enable flag." },
|
|
{ "scale", kScaleMtId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Scale tap times. (0.0 - 1.0)" },
|
|
{ "fb", kFbMtId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Feedback" },
|
|
{ "in", kInMtId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "out", kOutMtId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
|
|
};
|
|
|
|
// verify that at least one var arg exists
|
|
if( va_cnt < 5 || cmIsEvenU(va_cnt) )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The 'multi-tap delay requires at least 5 arguments. Three fixed arguments and groups of two tap specification arguments.");
|
|
return NULL;
|
|
}
|
|
|
|
va_copy(vl1,vl);
|
|
|
|
unsigned reqArgCnt = 3;
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned tapCnt = (va_cnt - reqArgCnt)/2;
|
|
cmReal_t* msV = cmMemAllocZ(cmReal_t,tapCnt);
|
|
cmReal_t* gainV = cmMemAllocZ(cmReal_t,tapCnt);
|
|
|
|
unsigned argCnt = fixArgCnt + 2 * tapCnt;
|
|
unsigned baseGainMtId = kBaseMsMtId + tapCnt;
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
unsigned i;
|
|
|
|
// Get the taps and gains
|
|
va_arg(vl1,int); // enable
|
|
va_arg(vl1,double); // time scale
|
|
va_arg(vl1,double); // feedback
|
|
for(i=0; i<tapCnt; ++i)
|
|
{
|
|
msV[i] = va_arg(vl1,double);
|
|
gainV[i] = va_arg(vl1,double);
|
|
}
|
|
|
|
|
|
// setup the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kBaseMsMtId, tapCnt, "ms", kBaseMsMtId, 0, 0, kInDsvFl | kDoubleDsvFl, "Tap delay times in milliseconds.");
|
|
cmDspArgSetupN(ctx, a, argCnt, baseGainMtId, tapCnt, "gain", baseGainMtId, 0, 0, kInDsvFl | kDoubleDsvFl, "Tap delay linear gain.");
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
cmDspMtDelay_t* p = cmDspInstAlloc(cmDspMtDelay_t,ctx,classPtr,a,instSymId,id,storeSymId,reqArgCnt,vl1);
|
|
|
|
|
|
p->p = cmMDelayAlloc(ctx->cmProcCtx,NULL,0, 0, 0, 0, NULL, NULL );
|
|
p->baseGainMtId = baseGainMtId;
|
|
p->tapCnt = tapCnt;
|
|
p->msV = msV;
|
|
p->gainV = gainV;
|
|
p->printSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"_print");
|
|
|
|
for(i=0; i<tapCnt; ++i)
|
|
{
|
|
cmDspSetDefaultDouble(ctx,&p->inst,kBaseMsMtId+i, 0.0, msV[i]);
|
|
cmDspSetDefaultDouble(ctx,&p->inst,baseGainMtId+i, 0.0, gainV[i]);
|
|
}
|
|
|
|
va_end(vl1);
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMtDelayFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspMtDelay_t* p = (cmDspMtDelay_t*)inst;
|
|
|
|
cmMemFree(p->msV);
|
|
cmMemFree(p->gainV);
|
|
|
|
cmMDelayFree(&p->p);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMtDelayReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspMtDelay_t* p = (cmDspMtDelay_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
cmMDelayInit(p->p,cmDspSamplesPerCycle(ctx), cmDspSampleRate(ctx), cmDspDouble(&p->inst,kFbMtId), p->tapCnt, p->msV, p->gainV );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMtDelayExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspMtDelay_t* p = (cmDspMtDelay_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInMtId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInMtId);
|
|
|
|
unsigned oChIdx = 0;
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutMtId,oChIdx);
|
|
unsigned oSmpCnt = cmDspVarRows(inst,kOutMtId);
|
|
|
|
bool bypassFl= cmDspBool(inst,kBypassMtId);
|
|
|
|
if( ip != NULL )
|
|
cmMDelayExec(p->p,ip,op,cmMin(iSmpCnt,oSmpCnt),bypassFl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMtDelayRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspMtDelay_t* p = (cmDspMtDelay_t*)inst;
|
|
cmDspRC_t rc= kOkDspRC;
|
|
|
|
cmDspSetEvent(ctx,inst,evt);
|
|
|
|
// set tap times
|
|
if( kBaseMsMtId <= evt->dstVarId && evt->dstVarId < kBaseMsMtId + p->p->delayCnt )
|
|
cmMDelaySetTapMs(p->p,evt->dstVarId - kBaseMsMtId, cmDspDouble(inst,evt->dstVarId));
|
|
else
|
|
// set tap gains
|
|
if( p->baseGainMtId <= evt->dstVarId && evt->dstVarId < p->baseGainMtId + p->p->delayCnt )
|
|
cmMDelaySetTapGain(p->p,evt->dstVarId - p->baseGainMtId, cmDspDouble(inst,evt->dstVarId));
|
|
else
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kScaleMtId:
|
|
//cmDspDouble(inst,kScaleMtId);
|
|
break;
|
|
|
|
case kFbMtId:
|
|
p->p->fbCoeff = cmDspDouble(inst,kFbMtId);
|
|
break;
|
|
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMtDelayRecvFunc( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned attrSymId, const cmDspValue_t* value )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspMtDelay_t* p = (cmDspMtDelay_t*)inst;
|
|
|
|
if( cmDsvIsSymbol(value) && (cmDsvSymbol(value)==p->printSymId) )
|
|
{
|
|
int i;
|
|
cmRptPrintf(ctx->rpt,"taps:%i\n",p->tapCnt);
|
|
for(i=0; i<p->tapCnt; ++i)
|
|
cmRptPrintf(ctx->rpt,"%f %f\n",p->msV[i],p->gainV[i]);
|
|
|
|
cmMDelayReport(p->p, ctx->rpt );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspClass_t* cmMtDelayClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmMtDelayDC,ctx,"MtDelay",
|
|
NULL,
|
|
_cmDspMtDelayAlloc,
|
|
_cmDspMtDelayFree,
|
|
_cmDspMtDelayReset,
|
|
_cmDspMtDelayExec,
|
|
_cmDspMtDelayRecv,
|
|
NULL,
|
|
_cmDspMtDelayRecvFunc,
|
|
"Multi-tap delay.");
|
|
|
|
return &_cmMtDelayDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspPShift file_desc:"Time-domain pitch shifter." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassPsId,
|
|
kRatioPsId,
|
|
kInPsId,
|
|
kOutPsId
|
|
};
|
|
|
|
cmDspClass_t _cmPShiftDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
//cmCtx* ctx;
|
|
cmPitchShift* pshift;
|
|
} cmDspPShift_t;
|
|
|
|
cmDspInst_t* _cmDspPShiftAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
unsigned chs = 1;
|
|
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "bypass",kBypassPsId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Bypass enable flag." },
|
|
{ "ratio", kRatioPsId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Ratio" },
|
|
{ "in", kInPsId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "out", kOutPsId, 0, chs, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
|
|
{ NULL, 0, 0, 0, 0 }
|
|
};
|
|
|
|
cmDspPShift_t* p = cmDspInstAlloc(cmDspPShift_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassPsId, 0, 0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst ,kRatioPsId, 0.0, 1.0 );
|
|
|
|
p->pshift = cmPitchShiftAlloc(ctx->cmProcCtx,NULL,cmDspSamplesPerCycle(ctx), cmDspSampleRate(ctx) );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPShiftFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspPShift_t* p = (cmDspPShift_t*)inst;
|
|
|
|
cmPitchShiftFree(&p->pshift);
|
|
|
|
//cmCtxFree(&p->ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPShiftReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPShiftExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspPShift_t* p = (cmDspPShift_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInPsId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInPsId);
|
|
|
|
unsigned oChIdx = 0;
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutPsId,oChIdx);
|
|
unsigned oSmpCnt = cmDspVarRows(inst,kOutPsId);
|
|
|
|
bool bypassFl= cmDspBool(inst,kBypassPsId);
|
|
|
|
cmPitchShiftExec(p->pshift,ip,op,cmMin(iSmpCnt,oSmpCnt),cmDspDouble(inst,kRatioPsId),bypassFl);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPShiftRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return cmDspSetEvent(ctx,inst,evt);
|
|
}
|
|
|
|
cmDspClass_t* cmPShiftClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmPShiftDC,ctx,"PShift",
|
|
NULL,
|
|
_cmDspPShiftAlloc,
|
|
_cmDspPShiftFree,
|
|
_cmDspPShiftReset,
|
|
_cmDspPShiftExec,
|
|
_cmDspPShiftRecv,
|
|
NULL,NULL,
|
|
"Pitch Shifter.");
|
|
|
|
return &_cmPShiftDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspLoopRecd file_desc:"Loop recorder." kw:[sunit] }
|
|
enum
|
|
{
|
|
kTimeLrId,
|
|
kPGainLrId,
|
|
kRGainLrId,
|
|
kBypassLrId,
|
|
kPlayLrId,
|
|
kRecdLrId,
|
|
kRatioLrId,
|
|
kInLrId,
|
|
kOutLrId
|
|
|
|
};
|
|
|
|
cmDspClass_t _cmLoopRecdDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
//cmCtx* ctx;
|
|
bool playFl;
|
|
cmLoopRecord* lrp;
|
|
} cmDspLoopRecd_t;
|
|
|
|
cmDspInst_t* _cmDspLoopRecdAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
unsigned chs = 1;
|
|
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "time", kTimeLrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Max record time in seconds" },
|
|
{ "pgain", kPGainLrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Pass-through gain."},
|
|
{ "rgain", kRGainLrId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Recorder out gain."},
|
|
{ "bypass",kBypassLrId,0, 0, kInDsvFl | kBoolDsvFl, "Bypass flag"},
|
|
{ "play", kPlayLrId, 0, 0, kInDsvFl | kBoolDsvFl, "Play gate flag" },
|
|
{ "recd", kRecdLrId, 0, 0, kInDsvFl | kBoolDsvFl, "Recd gate flag" },
|
|
{ "ratio", kRatioLrId, 0, 0, kInDsvFl | kDoubleDsvFl, "Playback speed ratio"},
|
|
{ "in", kInLrId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "out", kOutLrId, 0, chs, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
|
|
{ NULL, 0, 0, 0, 0 }
|
|
};
|
|
|
|
cmDspLoopRecd_t* p = cmDspInstAlloc(cmDspLoopRecd_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kTimeLrId, 0, 10 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kPGainLrId, 0, 1.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kRGainLrId, 0, 1.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassLrId, 0, 0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kPlayLrId, 0, 0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kRecdLrId, 0, 0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kRatioLrId, 0, 1.0);
|
|
|
|
p->lrp = cmLoopRecordAlloc(ctx->cmProcCtx,NULL,0,0,0 );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspLoopRecdFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspLoopRecd_t* p = (cmDspLoopRecd_t*)inst;
|
|
|
|
cmLoopRecordFree(&p->lrp);
|
|
|
|
//cmCtxFree(&p->ctx);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspLoopRecdReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspLoopRecd_t* p = (cmDspLoopRecd_t*)inst;
|
|
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
|
|
cmReal_t maxRecdTimeSecs = cmDspDefaultDouble(&p->inst,kTimeLrId);
|
|
unsigned maxRecdTimeSmps = floor(cmDspSampleRate(ctx) * maxRecdTimeSecs);
|
|
unsigned xfadeTimeSmps = floor(cmDspSampleRate(ctx) * 50.0/1000.0);
|
|
|
|
if( maxRecdTimeSmps != p->lrp->maxRecdSmpCnt || xfadeTimeSmps != p->lrp->xfadeSmpCnt )
|
|
cmLoopRecordInit(p->lrp,cmDspSamplesPerCycle(ctx),maxRecdTimeSmps,xfadeTimeSmps);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspLoopRecdExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspLoopRecd_t* p = (cmDspLoopRecd_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInLrId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInLrId);
|
|
|
|
unsigned oChIdx = 0;
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutLrId,oChIdx);
|
|
unsigned oSmpCnt = cmDspVarRows(inst,kOutLrId);
|
|
|
|
bool recdFl = cmDspBool(inst,kRecdLrId);
|
|
bool bypassFl = cmDspBool(inst,kBypassLrId);
|
|
double ratio = cmDspDouble(inst,kRatioLrId);
|
|
|
|
double rgain = cmDspDouble(inst,kRGainLrId); // recorder output gain
|
|
double pgain = cmDspDouble(inst,kPGainLrId); // pass through gain
|
|
|
|
if( ip != NULL && op != NULL )
|
|
cmLoopRecordExec(p->lrp,ip,op,cmMin(iSmpCnt,oSmpCnt), bypassFl, recdFl, p->playFl, ratio, pgain, rgain );
|
|
|
|
|
|
p->playFl = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspLoopRecdRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspLoopRecd_t* p = (cmDspLoopRecd_t*)inst;
|
|
cmDspRC_t rc = cmDspSetEvent(ctx,inst,evt);
|
|
|
|
|
|
switch(evt->dstVarId)
|
|
{
|
|
case kPlayLrId:
|
|
p->playFl = cmDspBool(inst,kPlayLrId);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmLoopRecdClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmLoopRecdDC,ctx,"LoopRecd",
|
|
NULL,
|
|
_cmDspLoopRecdAlloc,
|
|
_cmDspLoopRecdFree,
|
|
_cmDspLoopRecdReset,
|
|
_cmDspLoopRecdExec,
|
|
_cmDspLoopRecdRecv,
|
|
NULL,NULL,
|
|
"Loop recorder.");
|
|
|
|
return &_cmLoopRecdDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspRectify file_desc:"Full-wave rectifier." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassRcId,
|
|
kCoeffRcId,
|
|
kInRcId,
|
|
kOutRcId
|
|
};
|
|
|
|
cmDspClass_t _cmRectifyDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
} cmDspRectify_t;
|
|
|
|
cmDspInst_t* _cmDspRectifyAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
unsigned chs = 1;
|
|
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "bypass",kBypassRcId,0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Bypass enable flag." },
|
|
{ "coeff", kCoeffRcId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Coefficient" },
|
|
{ "in", kInRcId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "out", kOutRcId, 0, chs, kOutDsvFl | kAudioBufDsvFl, "Audio output." },
|
|
{ NULL, 0, 0, 0, 0 }
|
|
};
|
|
|
|
cmDspRectify_t* p = cmDspInstAlloc(cmDspRectify_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassRcId,0, 0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kCoeffRcId, 0, 0.0 );
|
|
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRectifyFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRectifyReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRectifyExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInRcId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInRcId);
|
|
|
|
unsigned oChIdx = 0;
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutRcId,oChIdx);
|
|
unsigned oSmpCnt = cmDspVarRows(inst,kOutRcId);
|
|
|
|
bool bypassFl= cmDspBool(inst,kBypassRcId);
|
|
unsigned n = cmMin(iSmpCnt,oSmpCnt);
|
|
|
|
unsigned i;
|
|
|
|
if( bypassFl )
|
|
memcpy(op,ip,n*sizeof(cmSample_t));
|
|
else
|
|
{
|
|
for(i=0; i<n; ++i)
|
|
op[i] = ip[i] > 0 ? ip[i] : 0;
|
|
}
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRectifyRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return cmDspSetEvent(ctx,inst,evt);
|
|
}
|
|
|
|
cmDspClass_t* cmRectifyClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmRectifyDC,ctx,"Rectify",
|
|
NULL,
|
|
_cmDspRectifyAlloc,
|
|
_cmDspRectifyFree,
|
|
_cmDspRectifyReset,
|
|
_cmDspRectifyExec,
|
|
_cmDspRectifyRecv,
|
|
NULL,NULL,
|
|
"Half-wave rectifier.");
|
|
|
|
return &_cmRectifyDC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspGateDetect file_desc:"Track the onset and offset of an incoming signal." kw:[sunit] }
|
|
enum
|
|
{
|
|
kWndMsGdId,
|
|
kOnThreshPctGdId,
|
|
kOnThreshDbGdId,
|
|
kOffThreshDbGdId,
|
|
kInGdId,
|
|
kGateGdId,
|
|
kRmsGdId,
|
|
kMeanGdId
|
|
};
|
|
|
|
cmDspClass_t _cmGateDetectDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
//cmCtx* ctx;
|
|
cmShiftBuf* sbp;
|
|
cmGateDetect* gdp;
|
|
|
|
|
|
} cmDspGateDetect_t;
|
|
|
|
cmDspInst_t* _cmDspGateDetectAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "wnd", kWndMsGdId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Window length in milliseconds." },
|
|
{ "onpct", kOnThreshPctGdId,0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Onset slope threshold [0.0 - 1.0]." },
|
|
{ "ondb", kOnThreshDbGdId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Onset threshold dB [-Inf to 0]" },
|
|
{ "offdb", kOffThreshDbGdId,0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Offset threshold dB [-Inf to 0]" },
|
|
{ "in", kInGdId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "gate", kGateGdId, 0, 0, kOutDsvFl | kBoolDsvFl, "Gate state output." },
|
|
{ "rms", kRmsGdId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Signal level RMS"},
|
|
{ "mean", kMeanGdId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Derr mean."},
|
|
{ NULL, 0, 0, 0, 0 }
|
|
};
|
|
|
|
cmDspGateDetect_t* p = cmDspInstAlloc(cmDspGateDetect_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
|
|
|
|
p->sbp = cmShiftBufAlloc(ctx->cmProcCtx,NULL,0,0,0);
|
|
p->gdp = cmGateDetectAlloc(ctx->cmProcCtx,NULL,0,0,0,0);
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kWndMsGdId, 0, 42 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOnThreshPctGdId, 0, 0.8 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOnThreshDbGdId, 0, -30.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOffThreshDbGdId, 0, -60.0 );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGateDetectFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspGateDetect_t* p = (cmDspGateDetect_t*)inst;
|
|
cmGateDetectFree(&p->gdp);
|
|
cmShiftBufFree(&p->sbp);
|
|
//cmCtxFree(&p->ctx);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGateDetectReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspGateDetect_t* p = (cmDspGateDetect_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) != kOkDspRC )
|
|
return rc;
|
|
|
|
double wndMs = cmDspDouble(inst,kWndMsGdId);
|
|
double sr = cmDspSampleRate(ctx);
|
|
unsigned wndSmpCnt = floor(wndMs * sr / 1000.0);
|
|
|
|
if( cmShiftBufInit(p->sbp, cmDspSamplesPerCycle(ctx), wndSmpCnt, wndSmpCnt/4 ) != cmOkRC )
|
|
return cmErrMsg(&ctx->cmCtx->err,kInstResetFailDspRC,"The gate detector shift buffer initialization failed.");
|
|
|
|
if( cmGateDetectInit(p->gdp, cmDspSamplesPerCycle(ctx), cmDspDouble(inst,kOnThreshPctGdId), cmDspDouble(inst,kOnThreshDbGdId), cmDspDouble(inst,kOffThreshDbGdId) ) != cmOkRC )
|
|
return cmErrMsg(&ctx->cmCtx->err,kInstResetFailDspRC,"The gate detector shift buffer initialization failed.");
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGateDetectExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspGateDetect_t* p = (cmDspGateDetect_t*)inst;
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInGdId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInGdId);
|
|
|
|
|
|
while( cmShiftBufExec(p->sbp, ip, iSmpCnt ) )
|
|
{
|
|
cmGateDetectExec(p->gdp,p->sbp->outV, p->sbp->outN );
|
|
|
|
cmDspSetDouble(ctx,inst,kRmsGdId,p->gdp->rms);
|
|
cmDspSetDouble(ctx,inst,kMeanGdId,p->gdp->mean);
|
|
|
|
if( p->gdp->deltaFl )
|
|
cmDspSetBool( ctx,inst,kGateGdId,p->gdp->gateFl);
|
|
|
|
}
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGateDetectRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
switch(evt->dstVarId)
|
|
{
|
|
case kWndMsGdId:
|
|
break;
|
|
case kOnThreshPctGdId:
|
|
break;
|
|
case kOnThreshDbGdId:
|
|
break;
|
|
case kOffThreshDbGdId:
|
|
break;
|
|
case kInGdId:
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmGateDetectClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmGateDetectDC,ctx,"GateDetect",
|
|
NULL,
|
|
_cmDspGateDetectAlloc,
|
|
_cmDspGateDetectFree,
|
|
_cmDspGateDetectReset,
|
|
_cmDspGateDetectExec,
|
|
_cmDspGateDetectRecv,
|
|
NULL,NULL,
|
|
"Gate detector.");
|
|
|
|
return &_cmGateDetectDC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspAutoGain file_desc:"Normalize a set of audio input signals to acheive a consistent level." kw:[sunit] }
|
|
// The purpose of this object is to calculate, store and retrieve gain coefficents
|
|
// for a set of audio channels. The gain coefficients are designed to balance the
|
|
// volume of each channel relative to the others. During gain calibration
|
|
// a sample of each channel is taken and it's average volume is determined.
|
|
// After an example of all channels has been received a new set of gain coefficients
|
|
// is calculated which decreases the volume of loud channels and increases the
|
|
// volume of quiet channels.
|
|
//
|
|
// The gain coefficents are made available via a set of 'gain-###' output ports.
|
|
//
|
|
// This object acts as an interface to the cmAutoGain processor.
|
|
//
|
|
// As input it takes a channel configuration JSON file of the form:
|
|
// {
|
|
// ch_array :
|
|
// [ ["ch","ssi","pitch","midi","gain"]
|
|
// [ 0, 0, "C4", 60, 1.0 ]
|
|
// ....
|
|
// [ n 0, "C5", 72, 1.0 ]
|
|
// ]
|
|
// }
|
|
//
|
|
// Each array in 'ch_array' gives the configuration of a channel.
|
|
//
|
|
//
|
|
// It also requires a JSON resource object of the form
|
|
// gdParms:
|
|
// {
|
|
// medCnt: 5
|
|
// avgCnt: 9
|
|
// suprCnt: 6
|
|
// offCnt: 3
|
|
// suprCoeff: 1.400000
|
|
// onThreshDb: -53.000000
|
|
// offThreshDb: -80.000000
|
|
// }
|
|
//
|
|
// These arguments are used to configure the cmAutoGain Proessor gate detector function.
|
|
//
|
|
// During runtime the object accepts the following action selector symbol id's:
|
|
// a. start - begin a new calibration session
|
|
// b. proc - end a calibration session and calculate new gain coeff's
|
|
// c. cancel - cancel a calibration session
|
|
// d. write - write the channel configuration file
|
|
// e. print - print the current auto gain calibration state
|
|
//
|
|
// After a 'start' msg the object accepts channel id's throught its 'id' input port.
|
|
// Each 'id' identifies the channel which it will process next.
|
|
// Upon reception of a channel id the object routes subsequent audio to its
|
|
// internal cmAutoGain processor until it receives the next channel id
|
|
// or a 'proc' or 'cancel' symbol.
|
|
|
|
|
|
//==========================================================================================================================================
|
|
enum
|
|
{
|
|
kChCntAgId,
|
|
kHopAgId,
|
|
kMedNAgId,
|
|
kAvgNAgId,
|
|
kSupNAgId,
|
|
kOffNAgId,
|
|
kSupCoefAgId,
|
|
kOnThrAgId,
|
|
kOffThrAgId,
|
|
kSelAgId,
|
|
kIdAgId,
|
|
kInBaseAgId
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmAutoGain* agp;
|
|
unsigned gainBaseAgId;
|
|
unsigned chCnt;
|
|
unsigned chIdx;
|
|
unsigned startSymId;
|
|
unsigned procSymId;
|
|
unsigned cancelSymId;
|
|
unsigned writeSymId;
|
|
unsigned printSymId;
|
|
} cmDspAutoGain_t;
|
|
|
|
cmDspClass_t _cmAutoGainDC;
|
|
|
|
cmDspInst_t* _cmDspAutoGainAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "chCnt",kChCntAgId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Audio channel count."},
|
|
{ "hop", kHopAgId, 0, 0, kDoubleDsvFl | kReqArgDsvFl, "RMS hop in milliseconds."},
|
|
{ "med", kMedNAgId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Median filter hop count."},
|
|
{ "avg", kAvgNAgId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Average filter hop count."},
|
|
{ "sup", kSupNAgId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Supression filter hop count."},
|
|
{ "off", kOffNAgId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Offset filter hop count."},
|
|
{ "supC", kSupCoefAgId,0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Suppression coefficent."},
|
|
{ "onThr",kOnThrAgId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Onset threshold in dB."},
|
|
{ "offThr",kOffThrAgId,0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Offset threshold in dB."},
|
|
{ "sel", kSelAgId, 0, 0, kInDsvFl | kSymDsvFl | kNoArgDsvFl, "Action Selector: start | proc | cancel." },
|
|
{ "id", kIdAgId, 0, 0, kInDsvFl | kIntDsvFl | kNoArgDsvFl, "Channel id input."},
|
|
};
|
|
|
|
va_list vl1;
|
|
unsigned i;
|
|
|
|
|
|
// verify that at least one var arg exists
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The AutoGain constructor must be given the audio channel count as its first argument.");
|
|
return NULL;
|
|
}
|
|
// copy the va_list so that it can be used again in cmDspInstAlloc()
|
|
va_copy(vl1,vl);
|
|
|
|
// get the first var arg which should be a filename
|
|
unsigned chCnt = va_arg(vl,unsigned);
|
|
|
|
if( chCnt == 0 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The AutoGain constructor requires at least 1 audio channel.");
|
|
va_end(vl1);
|
|
return NULL;
|
|
}
|
|
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned argCnt = fixArgCnt + 2 * chCnt;
|
|
unsigned gainBaseAgId = kInBaseAgId + chCnt;
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
|
|
assert( fixArgCnt == kInBaseAgId );
|
|
|
|
// setup the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kInBaseAgId, chCnt, "in", kInBaseAgId, 0, 0, kInDsvFl | kAudioBufDsvFl, "audio in");
|
|
cmDspArgSetupN(ctx, a, argCnt, gainBaseAgId, chCnt, "gain", gainBaseAgId, 0, 0, kOutDsvFl | kDoubleDsvFl, "calibrated channel gain");
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
// instantiate the object
|
|
cmDspAutoGain_t* p = cmDspInstAlloc(cmDspAutoGain_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1);
|
|
|
|
// assign the current gain coefficients
|
|
for(i=0; i<chCnt; ++i)
|
|
cmDspSetDefaultDouble( ctx, &p->inst, gainBaseAgId + i, 0.0, 1.0);
|
|
|
|
|
|
// allocate the auto gain calculation proc
|
|
p->agp = cmAutoGainAlloc(ctx->cmProcCtx,NULL,0,0,0,0,NULL);
|
|
p->chCnt = chCnt;
|
|
p->chIdx = cmInvalidIdx;
|
|
p->gainBaseAgId= gainBaseAgId;
|
|
p->startSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"start");
|
|
p->procSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"proc");
|
|
p->cancelSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"cancel");
|
|
p->printSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"print");
|
|
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kSelAgId, p->cancelSymId );
|
|
cmDspSetDefaultInt( ctx, &p->inst, kIdAgId, 0, cmInvalidId );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspAutoGainFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspAutoGain_t* p = (cmDspAutoGain_t*)inst;
|
|
|
|
cmAutoGainFree(&p->agp);
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAutoGainReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAutoGainExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspAutoGain_t* p = (cmDspAutoGain_t*)inst;
|
|
|
|
unsigned curInChIdx = cmDspInt( inst, kIdAgId);
|
|
|
|
if( cmDspSymbol( inst, kSelAgId ) == p->startSymId && curInChIdx != cmInvalidId )
|
|
{
|
|
unsigned inChVarId = kInBaseAgId+curInChIdx;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,inChVarId,0);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,inChVarId);
|
|
|
|
cmAutoGainProcCh( p->agp, ip, iSmpCnt );
|
|
}
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAutoGainInit( cmDspCtx_t* ctx, cmDspInst_t* inst )
|
|
{
|
|
cmDspAutoGain_t* p = (cmDspAutoGain_t*)inst;
|
|
cmGateDetectParams gd;
|
|
unsigned i;
|
|
|
|
// collect the params into a cmGateDetectParams recd
|
|
gd.medCnt = cmDspUInt( inst, kMedNAgId );
|
|
gd.avgCnt = cmDspUInt( inst, kAvgNAgId );
|
|
gd.suprCnt = cmDspUInt( inst, kSupNAgId );
|
|
gd.offCnt = cmDspUInt( inst, kOffNAgId );
|
|
gd.suprCoeff = cmDspDouble( inst, kSupCoefAgId );
|
|
gd.onThreshDb = cmDspDouble( inst, kOnThrAgId );
|
|
gd.offThreshDb = cmDspDouble( inst, kOffThrAgId );
|
|
|
|
// setup the internal auto-gain object
|
|
if( cmAutoGainInit(p->agp, cmDspSamplesPerCycle(ctx), cmDspSampleRate(ctx), cmDspDouble(inst,kHopAgId), p->chCnt, &gd ) != cmOkRC )
|
|
return cmDspInstErr(ctx,inst,kSubSysFailDspRC,"The internal auto-gain instance could not be initialized.");
|
|
|
|
// send out gain's of 1.0 so that the input audio is not
|
|
// biased by any existing scaling.
|
|
for(i=0; i<p->chCnt; ++i)
|
|
cmDspSetDouble( ctx, inst, p->gainBaseAgId+i, 1.0);
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAutoGainRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspAutoGain_t* p = (cmDspAutoGain_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kSelAgId:
|
|
{
|
|
// store the current 'sel' state
|
|
unsigned prvSymId = cmDspSymbol(inst,kSelAgId);
|
|
|
|
// update it 'sel' to the new state
|
|
cmDspSetEvent(ctx,inst,evt);
|
|
|
|
// get the new state
|
|
unsigned newSymId = cmDspSymbol(inst,kSelAgId);
|
|
|
|
//
|
|
// if PRINTing was requested
|
|
//
|
|
if( newSymId == p->printSymId )
|
|
{
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"Auto-Gain Report\n");
|
|
cmAutoGainPrint( p->agp, &ctx->cmCtx->rpt );
|
|
goto doneLabel;
|
|
}
|
|
|
|
|
|
//
|
|
// if calibration was CANCELLED
|
|
//
|
|
if( newSymId == p->cancelSymId )
|
|
{
|
|
cmDspSetInt( ctx, inst, kIdAgId, cmInvalidId );
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"cancelled\n");
|
|
goto doneLabel;
|
|
}
|
|
|
|
//
|
|
// if calibration STARTup was requested - initialize the autogain proc
|
|
//
|
|
if( newSymId == p->startSymId )
|
|
{
|
|
_cmDspAutoGainInit(ctx,inst);
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"started\n");
|
|
goto doneLabel;
|
|
}
|
|
|
|
//
|
|
// if calibration PROCessing was requested
|
|
//
|
|
if( newSymId == p->procSymId && prvSymId == p->startSymId )
|
|
{
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"proc\n");
|
|
|
|
// set the current channel id to 'cmInvalidId' to stop calls to
|
|
// cmAutoGainProcCh() in _cmDspAutoGainExec()
|
|
cmDspSetInt( ctx, inst, kIdAgId, cmInvalidId );
|
|
|
|
// update the auto gain coefficients
|
|
if( cmAutoGainCalcGains(p->agp) == cmOkRC )
|
|
{
|
|
// send the new auto gain coefficients to the output ports
|
|
unsigned i;
|
|
for(i=0; i<p->chCnt; ++i)
|
|
cmDspSetDouble( ctx, inst, p->gainBaseAgId+i, p->agp->chArray[i].gain);
|
|
}
|
|
|
|
goto doneLabel;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case kIdAgId:
|
|
cmDspSetEvent(ctx,inst,evt);
|
|
if( cmDspSymbol(inst,kSelAgId) == p->startSymId )
|
|
{
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"id:%i\n", cmDspInt(inst,kIdAgId));
|
|
cmAutoGainStartCh(p->agp, p->chIdx = cmDspInt(inst,kIdAgId));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{ assert(0); }
|
|
}
|
|
|
|
doneLabel:
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmAutoGainClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmAutoGainDC,ctx,"AutoGain",
|
|
NULL,
|
|
_cmDspAutoGainAlloc,
|
|
_cmDspAutoGainFree,
|
|
_cmDspAutoGainReset,
|
|
_cmDspAutoGainExec,
|
|
_cmDspAutoGainRecv,
|
|
NULL,NULL,
|
|
"Auto-gain calibrator.");
|
|
|
|
return &_cmAutoGainDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspEnvFollow file_desc:"Generate a control signal from by analyzing the power envelope of an incoming audio signal." kw:[sunit] }
|
|
enum
|
|
{
|
|
kHopMsEfId, // RMS window length in milliseconds
|
|
kMedCntEfId, //
|
|
kAvgCntEfId, //
|
|
kSuprCntEfId, //
|
|
kOffCntEfId, //
|
|
kSuprCoefEfId, //
|
|
kOnThrDbEfId, //
|
|
kOffThrDbEfId, //
|
|
kMaxDbEfId,
|
|
kInEfId,
|
|
kGateEfId,
|
|
kRmsEfId,
|
|
kLevelEfId,
|
|
kOnEfId,
|
|
kOffEfId
|
|
};
|
|
|
|
cmDspClass_t _cmEnvFollowDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmShiftBuf* sbp;
|
|
cmGateDetect2* gdp;
|
|
} cmDspEnvFollow_t;
|
|
|
|
cmDspInst_t* _cmDspEnvFollowAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "wnd", kHopMsEfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "RMS Window length"},
|
|
{ "med", kMedCntEfId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Median filter length." },
|
|
{ "avg", kAvgCntEfId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Averaging filter length." },
|
|
{ "sup", kSuprCntEfId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Supression filter length." },
|
|
{ "off", kOffCntEfId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Offset detection window length" },
|
|
{ "supc", kSuprCoefEfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Suppression shape coefficient" },
|
|
{ "ondb", kOnThrDbEfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Onset threshold dB." },
|
|
{ "offdb", kOffThrDbEfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Offset threshold dB" },
|
|
{ "maxdb", kMaxDbEfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Max reference dB" },
|
|
{ "in", kInEfId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input" },
|
|
{ "gate", kGateEfId, 0, 0, kOutDsvFl | kBoolDsvFl, "Gate state output." },
|
|
{ "rms", kRmsEfId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Signal level RMS"},
|
|
{ "level", kLevelEfId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Signal level 0.0-1.0 as scale between offset thresh dB and max dB"},
|
|
{ "ons", kOnEfId, 0, 0, kOutDsvFl | kUIntDsvFl, "Onset counter"},
|
|
{ "offs", kOffEfId, 0, 0, kOutDsvFl | kUIntDsvFl, "Offset counter"},
|
|
{ NULL, 0, 0, 0, 0 }
|
|
};
|
|
|
|
cmDspEnvFollow_t* p = cmDspInstAlloc(cmDspEnvFollow_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl);
|
|
|
|
p->sbp = cmShiftBufAlloc(ctx->cmProcCtx,NULL,0,0,0);
|
|
p->gdp = cmGateDetectAlloc2(ctx->cmProcCtx,NULL,0,NULL);
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kHopMsEfId, 0, 12 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kMedCntEfId, 0, 5 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kAvgCntEfId, 0, 9 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kSuprCntEfId, 0, 6 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kOffCntEfId, 0, 3 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kSuprCoefEfId, 0, 1.4 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOnThrDbEfId, 0, -45 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOffThrDbEfId, 0, -80 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kMaxDbEfId, 0, -10.0 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kOnEfId, 0, 0 );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kOffEfId, 0, 0 );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspEnvFollowFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspEnvFollow_t* p = (cmDspEnvFollow_t*)inst;
|
|
cmGateDetectFree2(&p->gdp);
|
|
cmShiftBufFree(&p->sbp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspEnvFollowReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspEnvFollow_t* p = (cmDspEnvFollow_t*)inst;
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) != kOkDspRC )
|
|
return rc;
|
|
|
|
cmGateDetectParams r;
|
|
r.medCnt = cmDspUInt( inst, kMedCntEfId );
|
|
r.avgCnt = cmDspUInt( inst, kAvgCntEfId );
|
|
r.suprCnt = cmDspUInt( inst, kSuprCntEfId );
|
|
r.offCnt = cmDspUInt( inst, kOffCntEfId );
|
|
r.suprCoeff = cmDspDouble( inst, kSuprCoefEfId );
|
|
r.onThreshDb = cmDspDouble( inst, kOnThrDbEfId );
|
|
r.offThreshDb = cmDspDouble( inst, kOffThrDbEfId );
|
|
|
|
double sr = cmDspSampleRate(ctx);
|
|
double hopSmpCnt = floor(sr * cmDspDouble(inst,kHopMsEfId) / 1000 );
|
|
unsigned wndSmpCnt = floor(r.medCnt * hopSmpCnt);
|
|
|
|
if( cmShiftBufInit(p->sbp, cmDspSamplesPerCycle(ctx), wndSmpCnt, hopSmpCnt ) != cmOkRC )
|
|
return cmDspInstErr(ctx,inst,kInstResetFailDspRC,"The gate detector shift buffer initialization failed.");
|
|
|
|
if( cmGateDetectInit2(p->gdp, cmDspSamplesPerCycle(ctx), &r ) != cmOkRC )
|
|
return cmDspInstErr(ctx,inst,kInstResetFailDspRC,"The gate detector shift buffer initialization failed.");
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspEnvFollowExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspEnvFollow_t* p = (cmDspEnvFollow_t*)inst;
|
|
unsigned iChIdx = 0;
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInEfId,iChIdx);
|
|
unsigned iSmpCnt = cmDspVarRows(inst,kInEfId);
|
|
double maxDb = cmDspDouble(inst,kMaxDbEfId);
|
|
double offDb = cmDspDouble(inst,kOffThrDbEfId);
|
|
|
|
while( cmShiftBufExec(p->sbp, ip, iSmpCnt ) )
|
|
{
|
|
cmGateDetectExec2(p->gdp,p->sbp->outV, p->sbp->outN );
|
|
|
|
// RMS is going out at the audio rate - maybe there should be an option
|
|
// to send it only when the gate changes - this could significantly
|
|
// cut down on unnecessary transmission if the RMS is only used
|
|
// when the gate changes
|
|
cmDspSetDouble(ctx,inst,kRmsEfId, p->gdp->rms );
|
|
|
|
double rmsDb = p->gdp->rms < 0.00001 ? -100.0 : 20.0 * log10(p->gdp->rms);
|
|
double level = maxDb <= offDb ? 0 : fabs((offDb - cmMax( offDb, cmMin( maxDb, rmsDb ))) / (maxDb - offDb));
|
|
|
|
cmDspSetDouble(ctx,inst,kLevelEfId, level );
|
|
|
|
if( p->gdp->onFl || p->gdp->offFl )
|
|
{
|
|
cmDspSetBool(ctx, inst, kGateEfId, p->gdp->gateFl);
|
|
|
|
if( p->gdp->onFl )
|
|
cmDspSetUInt( ctx, inst, kOnEfId, cmDspUInt(inst,kOnEfId) + 1 );
|
|
|
|
if( p->gdp->offFl )
|
|
cmDspSetUInt( ctx, inst, kOffEfId, cmDspUInt(inst,kOffEfId) + 1 );
|
|
}
|
|
}
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspEnvFollowRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
cmDspEnvFollow_t* p = (cmDspEnvFollow_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kOnThrDbEfId:
|
|
cmGateDetectSetOnThreshDb2(p->gdp, cmDspDouble(inst,kOnThrDbEfId) );
|
|
break;
|
|
|
|
case kOffThrDbEfId:
|
|
cmGateDetectSetOffThreshDb2(p->gdp, cmDspDouble(inst,kOffThrDbEfId) );
|
|
break;
|
|
}
|
|
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmEnvFollowClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmEnvFollowDC,ctx,"EnvFollow",
|
|
NULL,
|
|
_cmDspEnvFollowAlloc,
|
|
_cmDspEnvFollowFree,
|
|
_cmDspEnvFollowReset,
|
|
_cmDspEnvFollowExec,
|
|
_cmDspEnvFollowRecv,
|
|
NULL,NULL,
|
|
"Envelope follower and gate detector.");
|
|
|
|
return &_cmEnvFollowDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspXfader file_desc:"Gate controlled fader bank." kw:[sunit] }
|
|
// Fade in and out an arbitrary number of audio signals based on gate signals.
|
|
// When the gate is high the signal fades in and when the gate is low the signal fades out.
|
|
// Constructor Args:
|
|
// Required: Count of input and output channels.
|
|
// Optional: Fade time in milliseconds.
|
|
//
|
|
// Inputs:
|
|
// bool Control gates
|
|
// audio Input audio.
|
|
// Outputs:
|
|
// audio Output audio
|
|
// double Channel gains.
|
|
|
|
enum
|
|
{
|
|
kChCntXfId,
|
|
kFadeTimeMsXfId,
|
|
kMstrGateXfId,
|
|
kFadeInTimeMsXfId,
|
|
kFadeOutTimeMsXfId,
|
|
kResetXfId,
|
|
kOnXfId,
|
|
kOffXfId,
|
|
kGateBaseXfId,
|
|
};
|
|
|
|
cmDspClass_t _cmXfaderDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmXfader* xfdp;
|
|
unsigned inBaseXfId;
|
|
unsigned outBaseXfId;
|
|
unsigned stateBaseXfId;
|
|
unsigned gainBaseXfId;
|
|
unsigned chCnt;
|
|
bool* chGateV;
|
|
unsigned onSymId;
|
|
unsigned offSymId;
|
|
} cmDspXfader_t;
|
|
|
|
cmDspInst_t* _cmDspXfaderAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "chs", kChCntXfId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Input and Output channel count"},
|
|
{ "ms", kFadeTimeMsXfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Fade time in milliseonds."},
|
|
{ "mgate", kMstrGateXfId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Master gate - can be used to set all gates."},
|
|
{ "ims", kFadeInTimeMsXfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Fade in time in milliseonds."},
|
|
{ "oms", kFadeOutTimeMsXfId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Fade out time in milliseonds."},
|
|
{ "reset", kResetXfId, 0, 0, kInDsvFl | kBoolDsvFl, "Jump to gate states rather than fade."},
|
|
{ "on", kOnXfId, 0, 0, kOutDsvFl | kSymDsvFl, "Send 'on' when all ch's transition from off to on."},
|
|
{ "off", kOffXfId, 0, 0, kOutDsvFl | kSymDsvFl, "Send 'off' when all ch's transition from on to off."},
|
|
};
|
|
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The Xfader object must be given a channel count argument.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
unsigned chCnt = va_arg(vl,int);
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned argCnt = fixArgCnt + 5*chCnt;
|
|
unsigned inBaseXfId = kGateBaseXfId + chCnt;
|
|
unsigned outBaseXfId = inBaseXfId + chCnt;
|
|
unsigned stateBaseXfId = outBaseXfId + chCnt;
|
|
unsigned gainBaseXfId = stateBaseXfId + chCnt;
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
|
|
|
|
// setup the input gate detectors and the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kGateBaseXfId, chCnt, "gate", kGateBaseXfId, 0, 0, kInDsvFl | kBoolDsvFl, "gate flags");
|
|
cmDspArgSetupN(ctx, a, argCnt, inBaseXfId, chCnt, "in", inBaseXfId, 0, 0, kInDsvFl | kAudioBufDsvFl, "audio input");
|
|
cmDspArgSetupN(ctx, a, argCnt, outBaseXfId, chCnt, "out", outBaseXfId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "audio output");
|
|
cmDspArgSetupN(ctx, a, argCnt, stateBaseXfId, chCnt, "state",stateBaseXfId, 0, 0, kOutDsvFl | kBoolDsvFl, "current fader state");
|
|
cmDspArgSetupN(ctx, a, argCnt, gainBaseXfId, chCnt, "gain", gainBaseXfId, 0, 0, kOutDsvFl | kDoubleDsvFl, "gain output");
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
cmDspXfader_t* p = cmDspInstAlloc(cmDspXfader_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1);
|
|
|
|
double fadeTimeMs = cmDspDouble(&p->inst, kFadeTimeMsXfId );
|
|
p->xfdp = cmXfaderAlloc(ctx->cmProcCtx,NULL,cmDspSampleRate(ctx), chCnt, fadeTimeMs);
|
|
p->inBaseXfId = inBaseXfId;
|
|
p->outBaseXfId = outBaseXfId;
|
|
p->stateBaseXfId = stateBaseXfId;
|
|
p->gainBaseXfId = gainBaseXfId;
|
|
p->chCnt = chCnt;
|
|
p->chGateV = cmMemAllocZ(bool,p->chCnt);
|
|
p->onSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"on");
|
|
p->offSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"off");
|
|
|
|
// set default values for the parameters that were not explicitely set in the va_arg list
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kFadeTimeMsXfId, 0, 100 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kMstrGateXfId, false, false);
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kOnXfId, p->onSymId );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kOffXfId, p->offSymId );
|
|
|
|
int i;
|
|
for(i=0; i<chCnt; ++i)
|
|
cmDspSetDefaultBool( ctx, &p->inst, stateBaseXfId+i, false, false );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspXfaderFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspXfader_t* p = (cmDspXfader_t*)inst;
|
|
cmMemFree(p->chGateV);
|
|
cmXfaderFree(&p->xfdp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspXfaderReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
|
|
cmDspXfader_t* p = (cmDspXfader_t*)inst;
|
|
|
|
// TODO: zeroing of output audio buffers should be built into cmDspApplyAllDefaults().
|
|
unsigned i;
|
|
for(i=0; i<p->chCnt; ++i)
|
|
cmDspZeroAudioBuf(ctx,inst,p->outBaseXfId + i);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspXfaderExec(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = cmOkRC;
|
|
cmDspXfader_t* p = (cmDspXfader_t*)inst;
|
|
unsigned i;
|
|
|
|
// update the internal cross fader by providing it with new gate settings and generate new gain values
|
|
cmXfaderExec( p->xfdp, cmDspSamplesPerCycle(ctx), p->chGateV, p->chCnt );
|
|
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,p->outBaseXfId+i,0);
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,p->outBaseXfId+i,0);
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,p->inBaseXfId+i,0);
|
|
cmSample_t gain = (cmSample_t)p->xfdp->chArray[i].ep_gain;
|
|
|
|
if( op != NULL )
|
|
{
|
|
if( ip == NULL )
|
|
cmVOS_Zero(op,n);
|
|
else
|
|
cmVOS_MultVVS(op,n,ip,gain);
|
|
}
|
|
|
|
if( p->xfdp->chArray[i].onFl )
|
|
{
|
|
cmDspSetBool(ctx,inst,p->stateBaseXfId+i,true);
|
|
}
|
|
|
|
if( p->xfdp->chArray[i].offFl )
|
|
{
|
|
cmDspSetBool(ctx,inst,p->stateBaseXfId+i,false);
|
|
}
|
|
|
|
// send the gain output
|
|
cmDspSetDouble(ctx,inst,p->gainBaseXfId+i,gain);
|
|
|
|
}
|
|
|
|
if( p->xfdp->onFl )
|
|
cmDspSetSymbol(ctx,inst,kOnXfId,p->onSymId);
|
|
|
|
if( p->xfdp->offFl )
|
|
cmDspSetSymbol(ctx,inst,kOffXfId,p->offSymId);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspXfaderRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
cmDspXfader_t* p = (cmDspXfader_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kFadeTimeMsXfId:
|
|
// if this is an xfade time event then transfer the new xfade time to the xfade proc
|
|
cmXfaderSetXfadeTime(p->xfdp,cmDspDouble(inst,kFadeTimeMsXfId));
|
|
break;
|
|
|
|
case kMstrGateXfId:
|
|
{
|
|
bool fl = cmDspBool(inst,kMstrGateXfId);
|
|
unsigned i;
|
|
for(i=0; i<p->chCnt; ++i)
|
|
p->chGateV[i] = fl;
|
|
}
|
|
break;
|
|
|
|
case kFadeInTimeMsXfId:
|
|
cmXfaderSetXfadeInTime(p->xfdp,cmDspDouble(inst,kFadeInTimeMsXfId));
|
|
break;
|
|
|
|
case kFadeOutTimeMsXfId:
|
|
cmXfaderSetXfadeOutTime(p->xfdp,cmDspDouble(inst,kFadeOutTimeMsXfId));
|
|
break;
|
|
|
|
case kResetXfId:
|
|
{
|
|
cmXfaderExec( p->xfdp, cmDspSamplesPerCycle(ctx), p->chGateV, p->chCnt );
|
|
cmXfaderJumpToDestinationGain(p->xfdp);
|
|
// force the chGateV[] to match the xfaders state
|
|
int i;
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
bool gateFl = p->xfdp->chArray[i].gateFl;
|
|
p->chGateV[i] = gateFl;
|
|
cmDspSetBool( ctx,inst,p->stateBaseXfId + i, gateFl);
|
|
cmDspSetDouble(ctx,inst,p->gainBaseXfId + i, gateFl ? 1.0 : 0.0 );
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
// record gate changes into p->chGateV[] for later use in _cmDspXfaderExec().
|
|
if( kGateBaseXfId <= evt->dstVarId && evt->dstVarId < kGateBaseXfId + p->chCnt )
|
|
{
|
|
p->chGateV[ evt->dstVarId - kGateBaseXfId ] = cmDspBool( inst, evt->dstVarId );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmXfaderClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmXfaderDC,ctx,"Xfader",
|
|
NULL,
|
|
_cmDspXfaderAlloc,
|
|
_cmDspXfaderFree,
|
|
_cmDspXfaderReset,
|
|
_cmDspXfaderExec,
|
|
_cmDspXfaderRecv,
|
|
NULL,NULL,
|
|
"Cross fade gain generator.");
|
|
|
|
return &_cmXfaderDC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspChCfg file_desc:"Configure a 'fluxo' channel." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kFnCcId,
|
|
kSelCcId,
|
|
kDoneCcId,
|
|
kGainBaseCcId
|
|
};
|
|
|
|
cmDspClass_t _cmChCfgDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmChCfg* ccp;
|
|
unsigned midiBaseCcId;
|
|
unsigned hzBaseCcId;
|
|
unsigned chBaseCcId;
|
|
unsigned nsflBaseCcId;
|
|
unsigned nshzBaseCcId;
|
|
unsigned printSymId;
|
|
unsigned writeSymId;
|
|
unsigned nsCmdSymId;
|
|
unsigned hzCmdSymId;
|
|
unsigned resetSymId;
|
|
} cmDspChCfg_t;
|
|
|
|
cmDspInst_t* _cmDspChCfgAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "fn", kFnCcId, 0, 0, kStrzDsvFl| kReqArgDsvFl, "Channel configuration JSON file name."},
|
|
{ "sel", kSelCcId, 0, 0, kInDsvFl | kSymDsvFl, "Action selector: print | write | ns | reset"},
|
|
{ "done", kDoneCcId,0, 0, kOutDsvFl | kSymDsvFl, "Trigger following action."}
|
|
};
|
|
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The channel configuration object must be given a file name argument.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
const cmChar_t* chCfgFn = va_arg(vl,cmChar_t*);
|
|
|
|
cmChCfg* ccp = cmChCfgAlloc( ctx->cmProcCtx, NULL, ctx->cmCtx, chCfgFn );
|
|
|
|
if( ccp == NULL || ccp->chCnt==0 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The channel configuration object could not be initialized with the file name '%s'.",cmStringNullGuard(chCfgFn));
|
|
return NULL;
|
|
}
|
|
|
|
unsigned chCnt = ccp->chCnt;
|
|
unsigned nsChCnt = ccp->nsChCnt;
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned argCnt = fixArgCnt + 5*chCnt + nsChCnt;
|
|
unsigned midiBaseCcId = kGainBaseCcId + chCnt;
|
|
unsigned hzBaseCcId = midiBaseCcId + chCnt;
|
|
unsigned chBaseCcId = hzBaseCcId + chCnt;
|
|
unsigned nsflBaseCcId = chBaseCcId + chCnt;
|
|
unsigned nshzBaseCcId = nsflBaseCcId + chCnt;
|
|
cmDspChCfg_t* p = NULL;
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
unsigned i,j;
|
|
|
|
// setup the input gate detectors and the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kGainBaseCcId, chCnt, "gain", kGainBaseCcId, 0, 0, kSendDfltDsvFl | kInDsvFl | kOutDsvFl | kDoubleDsvFl, "Gain input and output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, midiBaseCcId, chCnt, "midi", midiBaseCcId, 0, 0, kSendDfltDsvFl | kOutDsvFl | kUIntDsvFl, "MIDI pitch output");
|
|
cmDspArgSetupN(ctx, a, argCnt, hzBaseCcId, chCnt, "hz", hzBaseCcId, 0, 0, kSendDfltDsvFl | kOutDsvFl | kDoubleDsvFl, "pitch output in Hz");
|
|
cmDspArgSetupN(ctx, a, argCnt, chBaseCcId, chCnt, "ch", chBaseCcId, 0, 0, kSendDfltDsvFl | kOutDsvFl | kUIntDsvFl , "Audio channel index");
|
|
cmDspArgSetupN(ctx, a, argCnt, nsflBaseCcId, chCnt, "nsfl", nsflBaseCcId, 0, 0, kOutDsvFl | kBoolDsvFl, "noise shaper enables");
|
|
cmDspArgSetupN(ctx, a, argCnt, nshzBaseCcId, nsChCnt, "nshz", nshzBaseCcId, 0, 0, kOutDsvFl | kDoubleDsvFl, "noise-shaper pitch output in Hz");
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
if((p = cmDspInstAlloc(cmDspChCfg_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1)) == NULL )
|
|
return NULL;
|
|
|
|
p->ccp = ccp;
|
|
p->midiBaseCcId = midiBaseCcId;
|
|
p->hzBaseCcId = hzBaseCcId;
|
|
p->chBaseCcId = chBaseCcId;
|
|
p->nsflBaseCcId = nsflBaseCcId;
|
|
p->nshzBaseCcId = nshzBaseCcId;
|
|
p->writeSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"write");
|
|
p->printSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"print");
|
|
p->nsCmdSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"ns");
|
|
p->hzCmdSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"hz");
|
|
p->resetSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"reset");
|
|
|
|
for(i=0,j=0; i<chCnt; ++i)
|
|
{
|
|
double hz = cmMidiToHz(ccp->chArray[i].midi);
|
|
cmDspSetDefaultDouble(ctx, &p->inst, kGainBaseCcId + i, 0.0, ccp->chArray[i].gain);
|
|
cmDspSetDefaultUInt( ctx, &p->inst, p->midiBaseCcId + i, 0, ccp->chArray[i].midi );
|
|
cmDspSetDefaultDouble(ctx, &p->inst, p->hzBaseCcId + i, 0.0, hz );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, p->chBaseCcId + i, 0, ccp->chArray[i].ch );
|
|
cmDspSetDefaultBool( ctx, &p->inst, p->nsflBaseCcId+i, false, false );
|
|
if( ccp->chArray[i].nsFl )
|
|
{
|
|
cmDspSetDefaultDouble(ctx,&p->inst, p->nshzBaseCcId+j, 0.0, hz);
|
|
++j;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
cmDspSetDefaultSymbol(ctx, &p->inst, kDoneCcId, cmInvalidId );
|
|
cmDspSetDefaultSymbol(ctx, &p->inst, kSelCcId, cmInvalidId );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspChCfgFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspChCfg_t* p = (cmDspChCfg_t*)inst;
|
|
cmChCfgFree(&p->ccp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspChCfgReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspChCfgRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
cmDspChCfg_t* p = (cmDspChCfg_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
if( evt->dstVarId == kSelCcId )
|
|
{
|
|
unsigned selId = cmDspSymbol(inst,kSelCcId);
|
|
|
|
if( selId == p->resetSymId )
|
|
{
|
|
_cmDspChCfgReset(ctx,inst,evt);
|
|
}
|
|
else
|
|
if( selId == p->hzCmdSymId )
|
|
{
|
|
unsigned i;
|
|
// snd the hz
|
|
for(i=0; i<p->ccp->chCnt; ++i)
|
|
cmDspSetDouble(ctx,inst,p->hzBaseCcId+i,cmDspDouble(inst,p->hzBaseCcId+i));
|
|
|
|
}
|
|
else
|
|
if( selId == p->nsCmdSymId )
|
|
{
|
|
cmRptPrintf(ctx->rpt,"ChCfg:NS\n");
|
|
|
|
unsigned i;
|
|
// send the ns flags
|
|
for(i=0; i<p->ccp->chCnt; ++i)
|
|
cmDspSetBool(ctx,inst,p->nsflBaseCcId+i,p->ccp->chArray[i].nsFl);
|
|
|
|
// snd the ns hz
|
|
for(i=0; i<p->ccp->nsChCnt; ++i)
|
|
cmDspSetDouble(ctx,inst,p->nshzBaseCcId+i,cmDspDouble(inst,p->nshzBaseCcId+i));
|
|
|
|
cmDspSetSymbol(ctx,inst,kDoneCcId,p->nsCmdSymId);
|
|
|
|
}
|
|
else
|
|
|
|
if( selId == p->printSymId )
|
|
{
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"Channel Cfg Report\n");
|
|
cmChCfgPrint(p->ccp, ctx->rpt );
|
|
}
|
|
else
|
|
{
|
|
if( selId == p->writeSymId )
|
|
{
|
|
unsigned i;
|
|
|
|
cmRptPrintf(&ctx->cmCtx->rpt,"writing\n");
|
|
|
|
// copy the gain values into the internal chCfg object ...
|
|
for(i=0; i<p->ccp->chCnt; ++i)
|
|
p->ccp->chArray[i].gain = cmDspDouble(inst,kGainBaseCcId+i);
|
|
|
|
// ... and write the object
|
|
cmChCfgWrite(p->ccp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmChCfgClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmChCfgDC,ctx,"ChCfg",
|
|
NULL,
|
|
_cmDspChCfgAlloc,
|
|
_cmDspChCfgFree,
|
|
_cmDspChCfgReset,
|
|
NULL,
|
|
_cmDspChCfgRecv,
|
|
NULL,NULL,
|
|
"PP Channel Configuration Object.");
|
|
|
|
return &_cmChCfgDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspChordDetect file_desc:"Detect a predefined chord based on signal gates." kw:[sunit] }
|
|
enum
|
|
{
|
|
kRsrcCdId,
|
|
kMaxTimeSpanCdId,
|
|
kMinNoteCntCdId,
|
|
kDetectCdId,
|
|
kCountCdId,
|
|
kGateBaseCdId
|
|
};
|
|
|
|
cmDspClass_t _cmChordDetectDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmChordDetect* cdp;
|
|
unsigned rmsBaseCdId;
|
|
unsigned chCnt;
|
|
bool* chGateV; // chGateV[ chCnt ]
|
|
cmReal_t* chRmsV; // chRmsV[ chCnt ]
|
|
unsigned* chEnaV; // chEnaV[ chCnt ]
|
|
unsigned count;
|
|
} cmDspChordDetect_t;
|
|
|
|
cmDspInst_t* _cmDspChordDetectAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "rsrc", kRsrcCdId, 0, 0, kStrzDsvFl | kReqArgDsvFl, "Channel enable flag array."},
|
|
{ "span", kMaxTimeSpanCdId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Max. onset time span."},
|
|
{ "notes", kMinNoteCntCdId, 0, 0, kInDsvFl | kUIntDsvFl | kOptArgDsvFl, "Min. note count per chord."},
|
|
{ "detect", kDetectCdId, 0, 0, kOutDsvFl | kBoolDsvFl, "Chord detect flag."},
|
|
{ "count", kCountCdId, 0, 0, kOutDsvFl | kUIntDsvFl, "Count of chords detected since last reset."}
|
|
};
|
|
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The chord detector must be given a channel enable flags array resource argument .");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
const cmChar_t* rsrc = va_arg(vl,const cmChar_t*);
|
|
unsigned* enaV = NULL;
|
|
unsigned chCnt = 0;
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &chCnt, &enaV, rsrc, NULL ) != kOkDspRC )
|
|
{
|
|
va_end(vl1);
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The chord detector channel index resource '%s' could not be read.",cmStringNullGuard(rsrc));
|
|
return NULL;
|
|
}
|
|
|
|
//cmRptPrintf(ctx->rpt,"cd %s chs:%i\n",rsrc,chCnt);
|
|
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned argCnt = fixArgCnt + 2*chCnt;
|
|
unsigned rmsBaseCdId = kGateBaseCdId + chCnt;
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
unsigned i;
|
|
cmDspChordDetect_t* p;
|
|
|
|
// setup the input gate detectors and the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kGateBaseCdId, chCnt, "gate", kGateBaseCdId, 0, 0, kInDsvFl | kOutDsvFl | kBoolDsvFl, "Channel gate input and output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, rmsBaseCdId, chCnt, "rms", rmsBaseCdId, 0, 0, kInDsvFl | kOutDsvFl | kDoubleDsvFl, "Channel RMS input and output");
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
if((p = cmDspInstAlloc(cmDspChordDetect_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1)) == NULL )
|
|
return NULL;
|
|
|
|
double dfltMaxTimeSpanMs = 50.0;
|
|
unsigned dfltMinNoteCnt = 2;
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kMaxTimeSpanCdId, 0.0, dfltMaxTimeSpanMs );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kMinNoteCntCdId, 0, dfltMinNoteCnt );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kDetectCdId, false, false );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kCountCdId, 0, 0 );
|
|
|
|
for(i=0; i<chCnt; ++i)
|
|
{
|
|
cmDspSetDefaultBool( ctx, &p->inst, kGateBaseCdId + i, false, false );
|
|
cmDspSetDefaultDouble(ctx, &p->inst, rmsBaseCdId + i, 0.0, 0.0 );
|
|
}
|
|
|
|
p->cdp = cmChordDetectAlloc( ctx->cmProcCtx, NULL, cmDspSampleRate(ctx), chCnt, cmDspDouble(&p->inst,kMaxTimeSpanCdId), cmDspUInt(&p->inst,kMinNoteCntCdId) );
|
|
p->rmsBaseCdId = rmsBaseCdId;
|
|
p->chCnt = chCnt;
|
|
p->chGateV = cmMemAllocZ(bool, chCnt);
|
|
p->chRmsV = cmMemAllocZ(cmReal_t, chCnt);
|
|
p->chEnaV = enaV;
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspChordDetectFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspChordDetect_t* p = (cmDspChordDetect_t*)inst;
|
|
cmChordDetectFree(&p->cdp);
|
|
cmMemFree(p->chGateV);
|
|
cmMemFree(p->chRmsV);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspChordDetectReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspChordDetectExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspChordDetect_t* p = (cmDspChordDetect_t*)inst;
|
|
|
|
cmChordDetectExec(p->cdp, cmDspSamplesPerCycle(ctx), p->chGateV, p->chRmsV, p->chCnt );
|
|
|
|
if( p->cdp->detectFl )
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
bool fl = p->cdp->chArray[i].chordFl;
|
|
|
|
cmDspSetBool( ctx, inst, kGateBaseCdId + i, fl );
|
|
cmDspSetDouble( ctx, inst, p->rmsBaseCdId + i, fl ? p->cdp->chArray[i].candRMS : 0 );
|
|
}
|
|
|
|
cmDspSetBool(ctx, inst, kDetectCdId, true);
|
|
cmDspSetUInt(ctx, inst, kCountCdId, cmDspUInt(inst,kCountCdId) + 1 );
|
|
}
|
|
|
|
cmVOB_Zero(p->chGateV,p->chCnt);
|
|
cmVOR_Zero(p->chRmsV,p->chCnt);
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspChordDetectRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspChordDetect_t* p = (cmDspChordDetect_t*)inst;
|
|
|
|
if( kGateBaseCdId <= evt->dstVarId && evt->dstVarId < kGateBaseCdId + p->chCnt )
|
|
{
|
|
unsigned idx = evt->dstVarId - kGateBaseCdId;
|
|
if( p->chEnaV[idx] )
|
|
p->chGateV[ idx ] = cmDsvGetBool(evt->valuePtr);
|
|
|
|
//cmRptPrintf(ctx->rpt,"cd gate:%i e:%i v:%i\n",idx,p->chEnaV[idx],p->chGateV[idx]);
|
|
|
|
}
|
|
else
|
|
if( p->rmsBaseCdId <= evt->dstVarId && evt->dstVarId < p->rmsBaseCdId + p->chCnt )
|
|
{
|
|
unsigned idx = evt->dstVarId - p->rmsBaseCdId;
|
|
if( p->chEnaV[idx] )
|
|
p->chRmsV[ idx ] = cmDsvGetReal( evt->valuePtr );
|
|
}
|
|
else
|
|
{
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kMaxTimeSpanCdId:
|
|
cmChordDetectSetSpanMs(p->cdp,cmDspDouble(inst,kMaxTimeSpanCdId));
|
|
break;
|
|
|
|
case kMinNoteCntCdId:
|
|
p->cdp->minNotesPerChord = cmDspUInt(inst,kMinNoteCntCdId);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmChordDetectClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmChordDetectDC,ctx,"ChordDetect",
|
|
NULL,
|
|
_cmDspChordDetectAlloc,
|
|
_cmDspChordDetectFree,
|
|
_cmDspChordDetectReset,
|
|
_cmDspChordDetectExec,
|
|
_cmDspChordDetectRecv,
|
|
NULL,NULL,
|
|
"Chord detector.");
|
|
|
|
return &_cmChordDetectDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspFader file_desc:"Single channel gate controlled fader." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kTimeFaId,
|
|
kGateFaId,
|
|
kInFaId,
|
|
kGainFaId,
|
|
kOutFaId
|
|
};
|
|
|
|
cmDspClass_t _cmFaderDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmFader* fdp;
|
|
} cmDspFader_t;
|
|
|
|
cmDspInst_t* _cmDspFaderAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "time", kTimeFaId, 0, 0, kDoubleDsvFl | kOptArgDsvFl, "Fade time in milliseconds."},
|
|
{ "gate", kGateFaId, 0, 0, kInDsvFl | kBoolDsvFl, "Gate control signal."},
|
|
{ "in", kInFaId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input."},
|
|
{ "gain", kGainFaId, 0, 0, kOutDsvFl | kDoubleDsvFl, "gain output."},
|
|
{ "out", kOutFaId, 0, 0, kOutDsvFl | kAudioBufDsvFl, "Audio out."},
|
|
{ NULL, 0, 0, 0, 0, NULL }
|
|
};
|
|
|
|
cmDspFader_t* p;
|
|
|
|
if((p = cmDspInstAlloc(cmDspFader_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl)) == NULL )
|
|
return NULL;
|
|
|
|
double dfltFadeTimeMs = 100.0;
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kTimeFaId, 0.0, dfltFadeTimeMs );
|
|
|
|
p->fdp = cmFaderAlloc(ctx->cmProcCtx, NULL, cmDspSampleRate(ctx), dfltFadeTimeMs );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspFaderFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspFader_t* p = (cmDspFader_t*)inst;
|
|
cmFaderFree(&p->fdp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspFaderReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspFader_t* p = (cmDspFader_t*)inst;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
cmDspZeroAudioBuf(ctx,inst,kOutFaId);
|
|
cmFaderSetFadeTime(p->fdp,cmDspDouble(inst,kTimeFaId));
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspFaderExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspFader_t* p = (cmDspFader_t*)inst;
|
|
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutFaId,0);
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutFaId,0);
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInFaId,0);
|
|
|
|
cmFaderExec(p->fdp,n,cmDspBool(inst,kGateFaId),false,ip,op);
|
|
|
|
|
|
cmDspSetDouble(ctx,inst,kGainFaId,p->fdp->gain);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspFaderRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspFader_t* p = (cmDspFader_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
if( evt->dstVarId == kTimeFaId )
|
|
cmFaderSetFadeTime(p->fdp,cmDspDouble(inst,kTimeFaId));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmFaderClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmFaderDC,ctx,"Fader",
|
|
NULL,
|
|
_cmDspFaderAlloc,
|
|
_cmDspFaderFree,
|
|
_cmDspFaderReset,
|
|
_cmDspFaderExec,
|
|
_cmDspFaderRecv,
|
|
NULL,NULL,
|
|
"Audio fade in/out controller.");
|
|
|
|
return &_cmFaderDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspNoteSelect file_desc:"'fluxo' gate based logic controller." kw:[sunit fluxo] }
|
|
enum
|
|
{
|
|
kChCntNsId,
|
|
kTrigNsId,
|
|
kDoneNsId,
|
|
kGateBaseNsId
|
|
};
|
|
|
|
enum
|
|
{
|
|
kGroupNonNsId,
|
|
kGroup0NsId,
|
|
kGroup1NsId
|
|
};
|
|
|
|
cmDspClass_t _cmNoteSelectDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned chCnt;
|
|
unsigned rmsBaseNsId;
|
|
unsigned gate0BaseNsId;
|
|
unsigned gate1BaseNsId;
|
|
unsigned gate2BaseNsId;
|
|
unsigned gate3BaseNsId;
|
|
unsigned gate4BaseNsId;
|
|
bool* chGateV; // chGateV[chCnt]
|
|
cmReal_t* chRmsV; // chRmsV[ chCnt ];
|
|
unsigned* chGroupV; // chGroupV[ chCnt ] (0=non-chord 1=low/high 2=middle)
|
|
unsigned count;
|
|
unsigned doneSymId;
|
|
} cmDspNoteSelect_t;
|
|
|
|
cmDspInst_t* _cmDspNoteSelectAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "ch_cnt", kChCntNsId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Channel count."},
|
|
{ "trig", kTrigNsId, 0, 0, kInDsvFl | kBoolDsvFl, "Trigger note selection."},
|
|
{ "done", kDoneNsId, 0, 0, kOutDsvFl | kSymDsvFl, "Sends 'done' after new set of outputs have been sent."},
|
|
};
|
|
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The note selector must be given a channel count argument .");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
|
|
unsigned CD0chanN = 0;
|
|
unsigned CD1chanN = 0;
|
|
unsigned* CD0chan = NULL;
|
|
unsigned* CD1chan = NULL;
|
|
const cmChar_t* CD0rsrc = "CD0chan";
|
|
const cmChar_t* CD1rsrc = "CD1chan";
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &CD0chanN, &CD0chan, CD0rsrc, NULL ) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The chord detector channel index resource '%s' could not be read.",cmStringNullGuard(CD0rsrc));
|
|
return NULL;
|
|
}
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &CD1chanN, &CD1chan, CD1rsrc, NULL ) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The chord detector channel index resource '%s' could not be read.",cmStringNullGuard(CD1rsrc));
|
|
return NULL;
|
|
}
|
|
|
|
va_copy(vl1,vl);
|
|
|
|
unsigned chCnt = va_arg(vl,unsigned);
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned argCnt = fixArgCnt + 7*chCnt;
|
|
unsigned rmsBaseNsId = kGateBaseNsId + 1 * chCnt;
|
|
unsigned gate0BaseNsId = kGateBaseNsId + 2 * chCnt;
|
|
unsigned gate1BaseNsId = kGateBaseNsId + 3 * chCnt;
|
|
unsigned gate2BaseNsId = kGateBaseNsId + 4 * chCnt;
|
|
unsigned gate3BaseNsId = kGateBaseNsId + 5 * chCnt;
|
|
unsigned gate4BaseNsId = kGateBaseNsId + 6 * chCnt;
|
|
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
unsigned i;
|
|
cmDspNoteSelect_t* p;
|
|
|
|
// setup the input gate detectors and the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kGateBaseNsId, chCnt, "gate", kGateBaseNsId, 0, 0, kInDsvFl | kBoolDsvFl, "Channel gate input.");
|
|
cmDspArgSetupN(ctx, a, argCnt, rmsBaseNsId, chCnt, "rms", rmsBaseNsId, 0, 0, kInDsvFl | kDoubleDsvFl, "Channel RMS input");
|
|
cmDspArgSetupN(ctx, a, argCnt, gate0BaseNsId, chCnt, "gate-0", gate0BaseNsId, 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 0 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, gate1BaseNsId, chCnt, "gate-1", gate1BaseNsId, 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 1 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, gate2BaseNsId, chCnt, "gate-2", gate2BaseNsId, 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 2 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, gate3BaseNsId, chCnt, "gate-3", gate3BaseNsId, 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 3 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, gate4BaseNsId, chCnt, "gate-4", gate4BaseNsId, 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 4 output.");
|
|
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
if((p = cmDspInstAlloc(cmDspNoteSelect_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1)) == NULL )
|
|
return NULL;
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kTrigNsId, false, false );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kDoneNsId, cmInvalidId );
|
|
|
|
|
|
p->rmsBaseNsId = rmsBaseNsId;
|
|
p->gate0BaseNsId = gate0BaseNsId;
|
|
p->gate1BaseNsId = gate1BaseNsId;
|
|
p->gate2BaseNsId = gate2BaseNsId;
|
|
p->gate3BaseNsId = gate3BaseNsId;
|
|
p->gate4BaseNsId = gate4BaseNsId;
|
|
|
|
p->chCnt = chCnt;
|
|
p->chGateV = cmMemAllocZ(bool,chCnt);
|
|
p->chRmsV = cmMemAllocZ(cmReal_t,chCnt);
|
|
p->chGroupV = cmMemAllocZ(unsigned,chCnt);
|
|
p->doneSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"done");
|
|
|
|
for(i=0; i<CD0chanN; ++i)
|
|
{
|
|
if( CD0chan[i] >= chCnt )
|
|
cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The chord detector resource array '%s' value %i is out of range %i.",cmStringNullGuard(CD0rsrc),CD0chan[i],chCnt);
|
|
else
|
|
p->chGroupV[ CD0chan[i] ] = kGroup0NsId;
|
|
}
|
|
|
|
for(i=0; i<CD1chanN; ++i)
|
|
{
|
|
if( CD1chan[i] >= chCnt )
|
|
cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The chord detector resource array '%s' value %i is out of range %i.",cmStringNullGuard(CD1rsrc),CD1chan[i],chCnt);
|
|
else
|
|
p->chGroupV[ CD1chan[i] ] = kGroup1NsId;
|
|
}
|
|
|
|
for(i=0; i<chCnt; ++i)
|
|
{
|
|
cmDspSetDefaultDouble(ctx, &p->inst, rmsBaseNsId+i, 0.0, 0.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, gate0BaseNsId+i, false, false );
|
|
cmDspSetDefaultBool( ctx, &p->inst, gate1BaseNsId+i, false, false );
|
|
cmDspSetDefaultBool( ctx, &p->inst, gate2BaseNsId+i, false, false );
|
|
cmDspSetDefaultBool( ctx, &p->inst, gate3BaseNsId+i, false, false );
|
|
// the non-chord channel selections should always be on
|
|
cmDspSetDefaultBool( ctx, &p->inst, gate4BaseNsId+i, false, p->chGroupV[i] == kGroupNonNsId );
|
|
}
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNoteSelectFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspNoteSelect_t* p = (cmDspNoteSelect_t*)inst;
|
|
cmMemFree(p->chGateV);
|
|
cmMemFree(p->chRmsV);
|
|
cmMemFree(p->chGroupV);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNoteSelectReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspNoteSelect_t* p = (cmDspNoteSelect_t*)inst;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
cmVOR_Zero(p->chRmsV,p->chCnt);
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspNoteSelectRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspNoteSelect_t* p = (cmDspNoteSelect_t*)inst;
|
|
|
|
// store incoming gate values
|
|
if( kGateBaseNsId <= evt->dstVarId && evt->dstVarId < kGateBaseNsId + p->chCnt )
|
|
p->chGateV[ evt->dstVarId - kGateBaseNsId ] = cmDsvGetBool(evt->valuePtr);
|
|
else
|
|
// store incoming RMS values
|
|
if( p->rmsBaseNsId <= evt->dstVarId && evt->dstVarId < p->rmsBaseNsId + p->chCnt )
|
|
p->chRmsV[ evt->dstVarId - p->rmsBaseNsId ] = cmDsvGetReal( evt->valuePtr );
|
|
else
|
|
{
|
|
// if a chord detection was triggered
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC && evt->dstVarId == kTrigNsId )
|
|
{
|
|
unsigned i;
|
|
cmReal_t maxRms = 0;
|
|
unsigned maxIdx = cmInvalidIdx;
|
|
|
|
for(i=1; i<p->chCnt; ++i)
|
|
{
|
|
// if this channel had an onset and is a possible chord note and is the max RMS chord note
|
|
if( p->chGroupV[i] != kGroupNonNsId && p->chGateV[i] && (maxIdx==cmInvalidIdx || p->chRmsV[i] > maxRms) )
|
|
{
|
|
maxRms = p->chRmsV[i];
|
|
maxIdx = i;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
bool fl = p->chGroupV[i] != kGroupNonNsId;
|
|
bool chosenFl = fl && i==maxIdx;
|
|
bool otherFl = fl && i!=maxIdx && p->chGateV[i];
|
|
bool cd0Fl = p->chGroupV[i]==kGroup0NsId && (!otherFl) && (!chosenFl);
|
|
bool cd1Fl = p->chGroupV[i]==kGroup1NsId && (!otherFl) && (!chosenFl);
|
|
|
|
// gate set 0: set output gate for max chord note
|
|
cmDspSetBool(ctx,inst,p->gate0BaseNsId+i, chosenFl );
|
|
|
|
// gate set 1: set output gate for non-max chord notes
|
|
cmDspSetBool(ctx,inst,p->gate1BaseNsId+i, otherFl );
|
|
|
|
// gate set 2: set output gate for non-chord notes
|
|
cmDspSetBool(ctx,inst,p->gate2BaseNsId+i, cd0Fl );
|
|
|
|
// gate set 3: set output gate for non-chord notes
|
|
cmDspSetBool(ctx,inst,p->gate3BaseNsId+i, cd1Fl);
|
|
|
|
// gate set 4: set output gate for non-chord notes
|
|
cmDspSetBool(ctx,inst,p->gate4BaseNsId+i, !fl );
|
|
|
|
}
|
|
|
|
// send the 'done' symbol to notify the gate receivers that the
|
|
// new set of gates is complete
|
|
cmDspSetSymbol(ctx,inst,kDoneNsId, p->doneSymId);
|
|
|
|
// zero the RMS vector
|
|
cmVOR_Zero(p->chRmsV,p->chCnt);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmNoteSelectClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmNoteSelectDC,ctx,"NoteSelect",
|
|
NULL,
|
|
_cmDspNoteSelectAlloc,
|
|
_cmDspNoteSelectFree,
|
|
_cmDspNoteSelectReset,
|
|
NULL,
|
|
_cmDspNoteSelectRecv,
|
|
NULL,NULL,
|
|
"Chord detector.");
|
|
|
|
return &_cmNoteSelectDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspNetNoteSelect file_desc:"'fluxo' transmit gate information over the UDP network." kw:[sunit fluxo] }
|
|
|
|
enum
|
|
{
|
|
kTrigNnId,
|
|
kDoneNnId,
|
|
kGateBaseNnId
|
|
};
|
|
|
|
|
|
cmDspClass_t _cmNetNoteSelectDC;
|
|
|
|
#define _cmNetNoteSelPortCnt (10)
|
|
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned chCnt;
|
|
unsigned rmsBaseNnId;
|
|
|
|
unsigned gateBaseNNId[ _cmNetNoteSelPortCnt ];
|
|
|
|
|
|
bool* chGateV; // chGateV[chCnt]
|
|
cmReal_t* chRmsV; // chRmsV[ chCnt ];
|
|
unsigned* portChCntV; // portChCntV[ 10 ]
|
|
unsigned* portBaseIdV; // portBaseIdV[ 10 ]
|
|
unsigned* chPortV; // chPortV[ chCnt ]
|
|
unsigned* chPortIdxV; // chPortIdxV[ chCnt ]
|
|
unsigned* ncPortV; // ncPortV[ chCnt ]
|
|
unsigned* ncPortIdxV; // ncPortIdxV[ chCnt ]
|
|
|
|
unsigned count;
|
|
unsigned doneSymId;
|
|
} cmDspNetNoteSelect_t;
|
|
|
|
cmDspInst_t* _cmDspNetNoteSelectAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
|
|
unsigned chCnt = 0;
|
|
const cmChar_t* label = NULL;
|
|
const cmChar_t* chPortRsrc = "nsChSelChV";
|
|
unsigned* chPortV = NULL;
|
|
const cmChar_t* chPortIdxRsrc = "nsChSelChIdxV";
|
|
unsigned* chPortIdxV = NULL;
|
|
const cmChar_t* ncPortRsrc = "nsNcSelChV";
|
|
unsigned* ncPortV = NULL;
|
|
const cmChar_t* ncPortIdxRsrc = "nsNcSelChIdxV";
|
|
unsigned* ncPortIdxV = NULL;
|
|
|
|
unsigned i,j,n;
|
|
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "trig", kTrigNnId, 0, 0, kInDsvFl | kBoolDsvFl, "Trigger note selection."},
|
|
{ "done", kDoneNnId, 0, 0, kOutDsvFl | kSymDsvFl, "Sends 'done' after new set of outputs have been sent."},
|
|
};
|
|
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &chCnt, &chPortV, label = chPortRsrc, NULL ) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kRsrcNotFoundDspRC,"The resource '%s' could not be read.",label);
|
|
return NULL;
|
|
}
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &n, &chPortIdxV, label = chPortIdxRsrc, NULL ) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kRsrcNotFoundDspRC,"The resource '%s' could not be read.",label);
|
|
return NULL;
|
|
}
|
|
|
|
assert(n == chCnt );
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &n, &ncPortV, label = ncPortRsrc, NULL ) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kRsrcNotFoundDspRC,"The resource '%s' could not be read.",label);
|
|
return NULL;
|
|
}
|
|
|
|
assert(n == chCnt );
|
|
|
|
if( cmDspRsrcUIntArray( ctx->dspH, &n, &ncPortIdxV, label = ncPortIdxRsrc, NULL ) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kRsrcNotFoundDspRC,"The resource '%s' could not be read.",label);
|
|
return NULL;
|
|
}
|
|
|
|
assert(n == chCnt );
|
|
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned rmsBaseNnId = kGateBaseNnId + chCnt;
|
|
|
|
// get the count of ch's on each port
|
|
unsigned* portChCntV = cmLhAllocZ( ctx->lhH, unsigned, _cmNetNoteSelPortCnt );
|
|
unsigned* portBaseIdV = cmLhAllocZ( ctx->lhH, unsigned, _cmNetNoteSelPortCnt );
|
|
for(i=0; i<_cmNetNoteSelPortCnt; ++i)
|
|
{
|
|
// get the count of ch's in the ith gate output port
|
|
portChCntV[i] = cmVOU_Count( chPortV, chCnt, i ) + cmVOU_Count( ncPortV, chCnt, i );
|
|
|
|
// ports 1 and 6 are duplicates of ports 0 and 5
|
|
if( i == 1 || i == 6 )
|
|
portChCntV[i] = portChCntV[i-1];
|
|
|
|
// set the base port id for this port
|
|
if( i > 0 )
|
|
portBaseIdV[i] = portBaseIdV[i-1] + portChCntV[i-1];
|
|
else
|
|
portBaseIdV[ i ] = rmsBaseNnId + chCnt;
|
|
|
|
}
|
|
|
|
|
|
unsigned argCnt = fixArgCnt + (2*chCnt) + cmVOU_Sum(portChCntV,_cmNetNoteSelPortCnt );
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
cmDspNetNoteSelect_t* p;
|
|
|
|
// setup the input gate detectors and the output gain args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kGateBaseNnId, chCnt, "gate", kGateBaseNnId, 0, 0, kInDsvFl | kBoolDsvFl, "Channel gate input.");
|
|
cmDspArgSetupN(ctx, a, argCnt, rmsBaseNnId, chCnt, "rms", rmsBaseNnId, 0, 0, kInDsvFl | kDoubleDsvFl, "Channel RMS input");
|
|
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[0], portChCntV[0], "gate-0", portBaseIdV[0], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 0 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[1], portChCntV[1], "gate-1", portBaseIdV[1], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 1 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[2], portChCntV[2], "gate-2", portBaseIdV[2], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 2 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[3], portChCntV[3], "gate-3", portBaseIdV[3], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 3 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[4], portChCntV[4], "gate-4", portBaseIdV[4], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 4 output.");
|
|
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[5], portChCntV[5], "gate-5", portBaseIdV[5], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 5 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[6], portChCntV[6], "gate-6", portBaseIdV[6], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 6 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[7], portChCntV[7], "gate-7", portBaseIdV[7], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 7 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[8], portChCntV[8], "gate-8", portBaseIdV[8], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 8 output.");
|
|
cmDspArgSetupN(ctx, a, argCnt, portBaseIdV[9], portChCntV[9], "gate-9", portBaseIdV[9], 0, 0, kOutDsvFl | kBoolDsvFl, "Channel gate set 9 output.");
|
|
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
if((p = cmDspInstAlloc(cmDspNetNoteSelect_t,ctx,classPtr,a,instSymId,id,storeSymId,0,vl)) == NULL )
|
|
return NULL;
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kTrigNnId, false, false );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kDoneNnId, cmInvalidId );
|
|
|
|
|
|
p->rmsBaseNnId = rmsBaseNnId;
|
|
|
|
p->chCnt = chCnt;
|
|
p->chGateV = cmMemAllocZ(bool,chCnt);
|
|
p->chRmsV = cmMemAllocZ(cmReal_t,chCnt);
|
|
p->portChCntV = portChCntV;
|
|
p->portBaseIdV = portBaseIdV;
|
|
p->doneSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"done");
|
|
p->chPortV = chPortV;
|
|
p->chPortIdxV = chPortIdxV;
|
|
p->ncPortV = ncPortV;
|
|
p->ncPortIdxV = ncPortIdxV;
|
|
|
|
for(i=0; i<chCnt; ++i)
|
|
{
|
|
cmDspSetDefaultBool( ctx, &p->inst, kGateBaseNnId+i, false, false );
|
|
cmDspSetDefaultDouble(ctx, &p->inst, rmsBaseNnId+i, 0.0, 0.0 );
|
|
}
|
|
|
|
for(i=0; i<_cmNetNoteSelPortCnt; ++i)
|
|
for(j=0; j<p->portChCntV[i]; ++j)
|
|
cmDspSetDefaultBool( ctx, &p->inst, p->portBaseIdV[i]+j, false, false );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNetNoteSelectFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspNetNoteSelect_t* p = (cmDspNetNoteSelect_t*)inst;
|
|
cmMemFree(p->chGateV);
|
|
cmMemFree(p->chRmsV);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNetNoteSelectReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspNetNoteSelect_t* p = (cmDspNetNoteSelect_t*)inst;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
cmVOR_Zero(p->chRmsV,p->chCnt);
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspNetNoteSelectRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspNetNoteSelect_t* p = (cmDspNetNoteSelect_t*)inst;
|
|
|
|
// store incoming gate values
|
|
if( kGateBaseNnId <= evt->dstVarId && evt->dstVarId < kGateBaseNnId + p->chCnt )
|
|
{
|
|
p->chGateV[ evt->dstVarId - kGateBaseNnId ] = cmDsvGetBool(evt->valuePtr);
|
|
|
|
//unsigned idx = evt->dstVarId - kGateBaseNnId;
|
|
//cmRptPrintf(ctx->rpt,"ns gate:%i %i\n",idx, p->chGateV[ idx ]);
|
|
}
|
|
else
|
|
// store incoming RMS values
|
|
if( p->rmsBaseNnId <= evt->dstVarId && evt->dstVarId < p->rmsBaseNnId + p->chCnt )
|
|
{
|
|
p->chRmsV[ evt->dstVarId - p->rmsBaseNnId ] = cmDsvGetReal( evt->valuePtr );
|
|
}
|
|
else
|
|
{
|
|
// if a chord detection was triggered
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC && evt->dstVarId == kTrigNnId )
|
|
{
|
|
unsigned i;
|
|
cmReal_t maxRms = 0;
|
|
unsigned maxIdx = cmInvalidIdx;
|
|
|
|
for(i=1; i<p->chCnt; ++i)
|
|
{
|
|
// if this channel had an onset and is a possible chord note and is the max RMS chord note
|
|
if( p->chGateV[i] && (maxIdx==cmInvalidIdx || p->chRmsV[i] > maxRms) )
|
|
{
|
|
maxRms = p->chRmsV[i];
|
|
maxIdx = i;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
bool chosenFl = i==maxIdx;
|
|
bool otherFl = i!=maxIdx && p->chGateV[i];
|
|
bool nonFl = chosenFl==false && otherFl==false;
|
|
unsigned k;
|
|
|
|
// if this is a chord channel
|
|
if( p->chPortV[i] != cmInvalidIdx )
|
|
{
|
|
// get the port associated with this chord note
|
|
k = p->chPortV[i];
|
|
|
|
assert( k+1 < _cmNetNoteSelPortCnt );
|
|
assert( p->chPortIdxV[i] < p->portChCntV[k] && p->chPortIdxV[i] < p->portChCntV[k+1] );
|
|
|
|
// set the chosen and other gate outputs based on the state of
|
|
// chosenFl and otherFl
|
|
cmDspSetBool(ctx,inst,p->portBaseIdV[ k ] + p->chPortIdxV[i],chosenFl);
|
|
cmDspSetBool(ctx,inst,p->portBaseIdV[ k+1 ] + p->chPortIdxV[i],otherFl);
|
|
}
|
|
|
|
// all channels have a 'single' note channel
|
|
assert( p->ncPortV[i] != cmInvalidIdx );
|
|
|
|
k = p->ncPortV[i];
|
|
|
|
assert( k < _cmNetNoteSelPortCnt );
|
|
assert( p->ncPortIdxV[i] < p->portChCntV[k] );
|
|
|
|
cmDspSetBool(ctx,inst,p->portBaseIdV[k] + p->ncPortIdxV[i],nonFl);
|
|
|
|
}
|
|
|
|
// send the 'done' symbol to notify the gate receivers that the
|
|
// new set of gates is complete
|
|
cmDspSetSymbol(ctx,inst,kDoneNnId, p->doneSymId);
|
|
|
|
// zero the RMS vector
|
|
cmVOR_Zero(p->chRmsV,p->chCnt);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmNetNoteSelectClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmNetNoteSelectDC,ctx,"NetNoteSelect",
|
|
NULL,
|
|
_cmDspNetNoteSelectAlloc,
|
|
_cmDspNetNoteSelectFree,
|
|
_cmDspNetNoteSelectReset,
|
|
NULL,
|
|
_cmDspNetNoteSelectRecv,
|
|
NULL,NULL,
|
|
"Chord detector.");
|
|
|
|
return &_cmNetNoteSelectDC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspCombFilt file_desc:"Comb and Inverse comb filter." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassCfId,
|
|
kMinHzCfId,
|
|
kFbFlCfId,
|
|
kHzCfId,
|
|
kAlphaCfId,
|
|
kInCfId,
|
|
kOutCfId
|
|
};
|
|
|
|
cmDspClass_t _cmCombFiltDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmCombFilt* cfp;
|
|
} cmDspCombFilt_t;
|
|
|
|
cmDspInst_t* _cmDspCombFiltAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "bypass",kBypassCfId, 0, 0, kInDsvFl | kBoolDsvFl | kReqArgDsvFl, "Bypass enable flag." },
|
|
{ "minhz", kMinHzCfId, 0, 0, kDoubleDsvFl | kReqArgDsvFl, "Minimum frequency limit."},
|
|
{ "fb", kFbFlCfId, 0, 0, kInDsvFl | kBoolDsvFl | kReqArgDsvFl, "Configure the filter in feedback mode."},
|
|
{ "hz", kHzCfId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Lowest comb frequency." },
|
|
{ "alpha", kAlphaCfId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Filter coefficent."},
|
|
{ "in", kInCfId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input."},
|
|
{ "out", kOutCfId, 0, 1, kOutDsvFl| kAudioBufDsvFl, "Audio out."},
|
|
{ NULL, 0, 0, 0, 0, NULL }
|
|
};
|
|
|
|
cmDspCombFilt_t* p;
|
|
|
|
if((p = cmDspInstAlloc(cmDspCombFilt_t,ctx,classPtr,args,instSymId,id,storeSymId,va_cnt,vl)) == NULL )
|
|
return NULL;
|
|
|
|
p->cfp = cmCombFiltAlloc(ctx->cmProcCtx, NULL,
|
|
cmDspSampleRate(ctx),
|
|
cmDspBool(&p->inst,kFbFlCfId),
|
|
cmDspDouble(&p->inst,kMinHzCfId),
|
|
cmDspDouble(&p->inst,kAlphaCfId),
|
|
cmDspDouble(&p->inst,kHzCfId),
|
|
cmDspBool(&p->inst,kBypassCfId));
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCombFiltFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspCombFilt_t* p = (cmDspCombFilt_t*)inst;
|
|
cmCombFiltFree(&p->cfp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCombFiltReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspCombFilt_t* p = (cmDspCombFilt_t*)inst;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
cmDspZeroAudioBuf(ctx,inst,kOutCfId);
|
|
|
|
cmCombFiltInit(p->cfp,
|
|
cmDspSampleRate(ctx),
|
|
cmDspBool(inst,kFbFlCfId),
|
|
cmDspDouble(inst,kMinHzCfId),
|
|
cmDspDouble(inst,kAlphaCfId),
|
|
cmDspDouble(inst,kHzCfId),
|
|
cmDspBool(inst,kBypassCfId));
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCombFiltExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspCombFilt_t* p = (cmDspCombFilt_t*)inst;
|
|
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutCfId,0);
|
|
cmSample_t* op = cmDspAudioBuf(ctx,inst,kOutCfId,0);
|
|
const cmSample_t* ip = cmDspAudioBuf(ctx,inst,kInCfId,0);
|
|
|
|
cmCombFiltExec(p->cfp,ip,op,n);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCombFiltRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspCombFilt_t* p = (cmDspCombFilt_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kHzCfId:
|
|
cmCombFiltSetHz(p->cfp,cmDspDouble(inst,evt->dstVarId));
|
|
//printf("%s hz:%f\n",cmSymTblLabel(ctx->stH,inst->symId),cmDspDouble(inst,evt->dstVarId));
|
|
break;
|
|
|
|
case kAlphaCfId:
|
|
cmCombFiltSetAlpha(p->cfp,cmDspDouble(inst,evt->dstVarId));
|
|
break;
|
|
|
|
case kBypassCfId:
|
|
p->cfp->bypassFl = cmDspBool(inst,evt->dstVarId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmCombFiltClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmCombFiltDC,ctx,"CombFilt",
|
|
NULL,
|
|
_cmDspCombFiltAlloc,
|
|
_cmDspCombFiltFree,
|
|
_cmDspCombFiltReset,
|
|
_cmDspCombFiltExec,
|
|
_cmDspCombFiltRecv,
|
|
NULL,NULL,
|
|
"Comb Filter");
|
|
|
|
return &_cmCombFiltDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspScalarOp file_desc:"Perform arithmetic functions on scalar values." kw:[sunit] }
|
|
enum
|
|
{
|
|
kPortCntSoId,
|
|
kOpSoId,
|
|
kOutSoId,
|
|
kBaseOpdSoId
|
|
};
|
|
|
|
cmDspClass_t _cmScalarOpDC;
|
|
struct cmDspScalarOp_str;
|
|
|
|
typedef cmDspRC_t (*_cmDspScalarOpFunc_t)(cmDspCtx_t* ctx, cmDspInst_t* inst );
|
|
|
|
|
|
typedef struct cmDspScalar_str
|
|
{
|
|
cmDspInst_t inst;
|
|
_cmDspScalarOpFunc_t func;
|
|
unsigned inPortCnt;
|
|
} cmDspScalarOp_t;
|
|
|
|
cmDspRC_t _cmDspScalarOpFuncMult(cmDspCtx_t* ctx, cmDspInst_t* inst )
|
|
{
|
|
cmDspScalarOp_t* p = (cmDspScalarOp_t*)inst;
|
|
double val = 1.0;
|
|
unsigned i;
|
|
for(i=0; i<p->inPortCnt; ++i)
|
|
val *= cmDspDouble( inst, kBaseOpdSoId+i );
|
|
|
|
cmDspSetDouble( ctx, inst, kOutSoId, val );
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspScalarOpFuncAdd(cmDspCtx_t* ctx, cmDspInst_t* inst )
|
|
{
|
|
cmDspScalarOp_t* p = (cmDspScalarOp_t*)inst;
|
|
double val = 0;
|
|
unsigned i;
|
|
for(i=0; i<p->inPortCnt; ++i)
|
|
val += cmDspDouble( inst, kBaseOpdSoId+i );
|
|
|
|
cmDspSetDouble( ctx, inst, kOutSoId, val );
|
|
return kOkDspRC;
|
|
}
|
|
|
|
// var args syntax: "<in_port_cnt> <op_string> <opd_label_0> <opd_dflt_val_0> <opd_label_1> <opd_dflt_val_1> ... <opd_label_n> <opd_dflt_val_n>
|
|
|
|
cmDspInst_t* _cmDspScalarOpAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "cnt", kPortCntSoId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Input port count" },
|
|
{ "op", kOpSoId, 0, 0, kStrzDsvFl | kReqArgDsvFl, "Operation symbol as a string."},
|
|
{ "out", kOutSoId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Operation output."},
|
|
};
|
|
|
|
cmDspScalarOp_t* p;
|
|
|
|
if( va_cnt < 2 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'ScalarOp' constructor must have a count of input ports and operation identifier string.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
unsigned inPortCnt = va_arg(vl,unsigned);
|
|
const cmChar_t* opIdStr = va_arg(vl,const cmChar_t*);
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned argCnt = fixArgCnt + inPortCnt;
|
|
cmDspVarArg_t a[ argCnt+1 ];
|
|
double dfltVal[ inPortCnt ];
|
|
unsigned i;
|
|
_cmDspScalarOpFunc_t fp = NULL;
|
|
|
|
// validate the count of input ports
|
|
if( inPortCnt == 0 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarNotValidDspRC,"The 'ScalarOp' constructor input port argument must be non-zero.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// locate the operation function
|
|
if( strcmp(opIdStr,"*") == 0 )
|
|
fp = _cmDspScalarOpFuncMult;
|
|
else
|
|
if( strcmp(opIdStr,"+") == 0 )
|
|
fp = _cmDspScalarOpFuncAdd;
|
|
|
|
// validate the operation function
|
|
if( fp == NULL )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarNotValidDspRC,"The 'ScalarOp' constructor operation string id '%s' did not match a known operation.",cmStringNullGuard(opIdStr));
|
|
goto errLabel;
|
|
}
|
|
|
|
// setup the fixed args
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
|
|
for(i=0; i<inPortCnt; ++i)
|
|
{
|
|
// get the operand label
|
|
const cmChar_t* label = va_arg(vl,const cmChar_t*);
|
|
|
|
// get the operand default value
|
|
dfltVal[i] = va_arg(vl,double);
|
|
|
|
// setup the arg recd
|
|
cmDspArgSetup(ctx,a + fixArgCnt + i, label, cmInvalidId, kBaseOpdSoId+i,0,0,kDoubleDsvFl|kInDsvFl,"Operand");
|
|
}
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
if((p = cmDspInstAlloc(cmDspScalarOp_t,ctx,classPtr,a,instSymId,id,storeSymId,2,vl1)) == NULL )
|
|
goto errLabel;
|
|
|
|
for(i=0; i<inPortCnt; ++i)
|
|
cmDspSetDefaultDouble(ctx,&p->inst,kBaseOpdSoId+i,0.0,dfltVal[i]);
|
|
|
|
p->inPortCnt = inPortCnt;
|
|
p->func = fp;
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
|
|
errLabel:
|
|
va_end(vl1);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspScalarOpReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspScalarOpRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspScalarOp_t* p = (cmDspScalarOp_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
if( evt->dstVarId == kBaseOpdSoId )
|
|
p->func(ctx,inst);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmScalarOpClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmScalarOpDC,ctx,"ScalarOp",
|
|
NULL,
|
|
_cmDspScalarOpAlloc,
|
|
NULL,
|
|
_cmDspScalarOpReset,
|
|
NULL,
|
|
_cmDspScalarOpRecv,
|
|
NULL,NULL,
|
|
"Scalar Operations");
|
|
|
|
return &_cmScalarOpDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspGroupSel file_desc:"Select one group of audio channels from a set of audio channel groups." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kChCntGsId,
|
|
kGroupCntGsId,
|
|
kChsPerGroupGsId,
|
|
kBaseGateGsId,
|
|
};
|
|
|
|
cmDspClass_t _cmGroupSelDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned chCnt;
|
|
unsigned groupCnt;
|
|
cmGroupSel* gsp;
|
|
unsigned baseRmsGsId;
|
|
unsigned baseOutGsId;
|
|
} cmDspGroupSel_t;
|
|
|
|
cmDspInst_t* _cmDspGroupSelAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspVarArg_t args[] =
|
|
{
|
|
{ "chCnt", kChCntGsId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Channel count." },
|
|
{ "groupCnt", kGroupCntGsId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Group count." },
|
|
{ "chsPerGroup", kChsPerGroupGsId, 0, 0, kUIntDsvFl | kInDsvFl | kReqArgDsvFl, "Channels per group." }
|
|
};
|
|
|
|
if( va_cnt < 2 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'GroupSel' constructor must have a channel and group count.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
cmDspGroupSel_t* p;
|
|
unsigned i;
|
|
unsigned chCnt = va_arg(vl,unsigned);
|
|
unsigned groupCnt = va_arg(vl,unsigned);
|
|
unsigned outCnt = chCnt * groupCnt;
|
|
unsigned fixArgCnt = sizeof(args)/sizeof(args[0]);
|
|
unsigned baseRmsGsId = kBaseGateGsId + chCnt;
|
|
unsigned baseOutGsId = baseRmsGsId + chCnt;
|
|
unsigned argCnt = baseOutGsId + outCnt;
|
|
cmDspVarArg_t a[ argCnt + 1 ];
|
|
|
|
|
|
cmDspArgCopy( a, argCnt, 0, args, fixArgCnt );
|
|
cmDspArgSetupN(ctx, a, argCnt, kBaseGateGsId, chCnt, "gate", kBaseGateGsId, 0, 0, kInDsvFl | kBoolDsvFl, "Channel gate input.");
|
|
cmDspArgSetupN(ctx, a, argCnt, baseRmsGsId, chCnt, "rms", baseRmsGsId, 0, 0, kInDsvFl | kDoubleDsvFl, "Channel RMS input");
|
|
|
|
for(i=0; i<groupCnt; ++i)
|
|
{
|
|
int labelCharCnt = 31;
|
|
char label[ labelCharCnt + 1 ];
|
|
snprintf(label,labelCharCnt,"gate-%i",i);
|
|
cmDspArgSetupN(ctx, a, argCnt, baseOutGsId + (i*chCnt), chCnt, label, baseOutGsId + (i*chCnt), 0, 0, kOutDsvFl | kBoolDsvFl, "Output gates");
|
|
}
|
|
cmDspArgSetupNull( a+argCnt); // set terminating arg. flag
|
|
|
|
if((p = cmDspInstAlloc(cmDspGroupSel_t,ctx,classPtr,a,instSymId,id,storeSymId,va_cnt,vl1)) == NULL )
|
|
{
|
|
va_end(vl1);
|
|
return NULL;
|
|
}
|
|
|
|
p->chCnt = chCnt;
|
|
p->groupCnt = groupCnt;
|
|
p->gsp = cmGroupSelAlloc(ctx->cmProcCtx, NULL, 0, 0, 0 );
|
|
p->baseRmsGsId = baseRmsGsId;
|
|
p->baseOutGsId = baseOutGsId;
|
|
|
|
for(i=0; i<chCnt; ++i)
|
|
{
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBaseGateGsId, false, false );
|
|
cmDspSetDefaultDouble(ctx,&p->inst, baseRmsGsId, 0.0, 0.0 );
|
|
}
|
|
for(i=0; i<outCnt; ++i)
|
|
cmDspSetDefaultBool( ctx, &p->inst, baseOutGsId, false, false );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGroupSelFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspGroupSel_t* p = (cmDspGroupSel_t*)inst;
|
|
cmGroupSelFree(&p->gsp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGroupSelReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspGroupSel_t* p = (cmDspGroupSel_t*)inst;
|
|
rc = cmDspApplyAllDefaults(ctx,inst);
|
|
cmGroupSelInit(p->gsp,p->chCnt,p->groupCnt,cmDspUInt(&p->inst,kChsPerGroupGsId));
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGroupSelExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspGroupSel_t* p = (cmDspGroupSel_t*)inst;
|
|
|
|
if( cmGroupSelExec(p->gsp) == cmOkRC && p->gsp->updateFl )
|
|
{
|
|
unsigned i,j;
|
|
for(i=0; i<p->groupCnt; ++i)
|
|
{
|
|
cmGroupSelGrp* gp = p->gsp->groupArray + i;
|
|
|
|
if( gp->releaseFl )
|
|
{
|
|
for(j=0; j<gp->chIdxCnt; ++j)
|
|
cmDspSetBool(ctx,inst,p->baseOutGsId + (i*p->chCnt) + gp->chIdxArray[j], false);
|
|
}
|
|
|
|
if( gp->createFl )
|
|
{
|
|
for(j=0; j<gp->chIdxCnt; ++j)
|
|
cmDspSetBool(ctx,inst,p->baseOutGsId + (i*p->chCnt) + gp->chIdxArray[j], true);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspGroupSelRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspGroupSel_t* p = (cmDspGroupSel_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
if( evt->dstVarId == kChsPerGroupGsId )
|
|
p->gsp->chsPerGroup = cmDspUInt(inst, kChsPerGroupGsId );
|
|
else
|
|
if( kBaseGateGsId <= evt->dstVarId && evt->dstVarId < (kBaseGateGsId + p->chCnt) )
|
|
cmGroupSetChannelGate(p->gsp, evt->dstVarId - kBaseGateGsId, cmDspDouble(inst,evt->dstVarId));
|
|
else
|
|
if( p->baseRmsGsId <= evt->dstVarId && evt->dstVarId < (p->baseRmsGsId + p->chCnt) )
|
|
cmGroupSetChannelRMS(p->gsp, evt->dstVarId - p->baseRmsGsId, cmDspDouble(inst,evt->dstVarId));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmGroupSelClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmGroupSelDC,ctx,"GroupSel",
|
|
NULL,
|
|
_cmDspGroupSelAlloc,
|
|
_cmDspGroupSelFree,
|
|
_cmDspGroupSelReset,
|
|
_cmDspGroupSelExec,
|
|
_cmDspGroupSelRecv,
|
|
NULL,NULL,
|
|
"Group selector.");
|
|
|
|
return &_cmGroupSelDC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspNofM file_desc:"Select N channels from a set of M channels based on their current gate states." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kInChCntNmId,
|
|
kOutChCntNmId,
|
|
kFadeTimeNmId,
|
|
kBaseGateNmId,
|
|
};
|
|
|
|
cmDspClass_t _cmAudioNofM_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned inChCnt;
|
|
unsigned outChCnt;
|
|
cmAudioNofM* nmp;
|
|
unsigned baseInNmId;
|
|
unsigned baseOutNmId;
|
|
unsigned baseGainNmId;
|
|
} cmDspAudioNofM_t;
|
|
|
|
cmDspInst_t* _cmDspAudioNofM_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 2 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'AudioNofM' constructor must given input and output channel counts.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int inChCnt = va_arg(vl,int);
|
|
int outChCnt = va_arg(vl,int);
|
|
unsigned baseInNmId = kBaseGateNmId + inChCnt;
|
|
unsigned baseOutNmId = baseInNmId + inChCnt;
|
|
unsigned baseGainNmId= baseOutNmId + outChCnt;
|
|
unsigned i;
|
|
|
|
cmDspAudioNofM_t* p = cmDspInstAllocV(cmDspAudioNofM_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "ichs", kInChCntNmId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Input channel count.",
|
|
1, "ochs", kOutChCntNmId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Output channel count.",
|
|
1, "time", kFadeTimeNmId, 0, 0, kDoubleDsvFl | kOptArgDsvFl | kInDsvFl, "Fade time in milliseconds.",
|
|
inChCnt, "gate", kBaseGateNmId, 0, 0, kBoolDsvFl | kInDsvFl, "Gate inputs.",
|
|
inChCnt, "in", baseInNmId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input",
|
|
outChCnt, "out", baseOutNmId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output",
|
|
outChCnt, "gain", baseGainNmId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Gain output",
|
|
0 );
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kFadeTimeNmId, 0.0, 25.0 );
|
|
|
|
p->inChCnt = inChCnt;
|
|
p->outChCnt = outChCnt;
|
|
p->nmp = cmAudioNofMAlloc(ctx->cmProcCtx,NULL,0,0,0,0);
|
|
p->baseInNmId = baseInNmId;
|
|
p->baseOutNmId = baseOutNmId;
|
|
p->baseGainNmId = baseGainNmId;
|
|
|
|
for(i=0; i<outChCnt; ++i)
|
|
cmDspSetDefaultDouble( ctx, &p->inst, baseGainNmId + i, 0.0, 0.0 );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAudioNofM_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspAudioNofM_t* p = (cmDspAudioNofM_t*)inst;
|
|
cmAudioNofMFree(&p->nmp);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAudioNofM_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAudioNofM_t* p = (cmDspAudioNofM_t*)inst;
|
|
unsigned i;
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
for(i=0; i<p->outChCnt; ++i)
|
|
cmDspZeroAudioBuf(ctx,inst,p->baseOutNmId+i);
|
|
|
|
cmAudioNofMInit(p->nmp, cmDspSampleRate(ctx), p->inChCnt, p->outChCnt, cmDspDouble(&p->inst, kFadeTimeNmId ));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAudioNofM_Exec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAudioNofM_t* p = (cmDspAudioNofM_t*)inst;
|
|
unsigned i;
|
|
const cmSample_t* x[ p->inChCnt ];
|
|
cmSample_t* y[ p->outChCnt ];
|
|
unsigned n = 0;
|
|
|
|
for(i=0; i<p->inChCnt; ++i)
|
|
{
|
|
if( i==0 )
|
|
n = cmDspAudioBufSmpCount(ctx,inst,p->baseInNmId+i,0);
|
|
else
|
|
{ assert( n == cmDspAudioBufSmpCount(ctx,inst,p->baseInNmId+i,0)); }
|
|
|
|
x[i] = cmDspAudioBuf(ctx,inst,p->baseInNmId+i,0);
|
|
}
|
|
|
|
for(i=0; i<p->outChCnt; ++i)
|
|
{
|
|
y[i] = cmDspAudioBuf(ctx,inst,p->baseOutNmId+i,0);
|
|
|
|
assert( n == cmDspAudioBufSmpCount(ctx,inst,p->baseOutNmId+i,0));
|
|
|
|
cmVOS_Zero(y[i],n);
|
|
}
|
|
|
|
cmAudioNofMExec(p->nmp,x,p->inChCnt,y,p->outChCnt,n);
|
|
|
|
|
|
for(i=0; i<p->outChCnt; ++i)
|
|
{
|
|
cmAudioNofM_In* ip = p->nmp->outArray[i].list;
|
|
double v = 0;
|
|
|
|
for(; ip != NULL; ip=ip->link)
|
|
v += ip->fader->gain;
|
|
|
|
cmDspSetDouble(ctx, inst,p->baseGainNmId + i,v );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAudioNofM_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAudioNofM_t* p = (cmDspAudioNofM_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
if( kBaseGateNmId <= evt->dstVarId && evt->dstVarId <= kBaseGateNmId + p->inChCnt )
|
|
cmAudioNofMSetChannelGate( p->nmp, evt->dstVarId - kBaseGateNmId, cmDspBool(inst,evt->dstVarId) );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmAudioNofMClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmAudioNofM_DC,ctx,"AudioNofM",
|
|
NULL,
|
|
_cmDspAudioNofM_Alloc,
|
|
_cmDspAudioNofM_Free,
|
|
_cmDspAudioNofM_Reset,
|
|
_cmDspAudioNofM_Exec,
|
|
_cmDspAudioNofM_Recv,
|
|
NULL,NULL,
|
|
"Audio N of M Switch");
|
|
|
|
return &_cmAudioNofM_DC;
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspRingMod file_desc:"Ring modulator." kw:[sunit] }
|
|
enum
|
|
{
|
|
kInChCntRmId,
|
|
kBypassRmId,
|
|
kGainRmId,
|
|
kOutRmId,
|
|
kBaseInRmId
|
|
};
|
|
|
|
cmDspClass_t _cmRingModDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned inChCnt;
|
|
} cmDspRingMod_t;
|
|
|
|
cmDspInst_t* _cmDspRingModAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'RingMod' constructor must given an input channel counts.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int inChCnt = va_arg(vl,int);
|
|
|
|
cmDspRingMod_t* p = cmDspInstAllocV(cmDspRingMod_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "ichs", kInChCntRmId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Input channel count.",
|
|
1, "bypass",kBypassRmId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Bypass enable",
|
|
1, "gain", kGainRmId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain (default:1.0)",
|
|
1, "out", kOutRmId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output",
|
|
inChCnt, "in", kBaseInRmId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input",
|
|
0 );
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassRmId, false, false );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kGainRmId, 0.0, 1.0 );
|
|
|
|
p->inChCnt = inChCnt;
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspRingModReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
cmDspZeroAudioBuf(ctx,inst,kOutRmId);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRingModExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspRingMod_t* p = (cmDspRingMod_t*)inst;
|
|
unsigned i,j;
|
|
cmSample_t* y = cmDspAudioBuf(ctx,inst,kOutRmId, 0);
|
|
const cmSample_t* x0 = cmDspAudioBuf(ctx,inst,kBaseInRmId,0);
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutRmId,0);
|
|
double gain = cmDspDouble(inst,kGainRmId);
|
|
bool bypassFl = cmDspBool(inst,kBypassRmId);
|
|
|
|
for(i=1; i<p->inChCnt; ++i)
|
|
{
|
|
assert( n == cmDspAudioBufSmpCount(ctx,inst,kBaseInRmId+i,0));
|
|
|
|
const cmSample_t* x1 = cmDspAudioBuf(ctx,inst,kBaseInRmId+i,0);
|
|
|
|
if( bypassFl )
|
|
{
|
|
for(j=0; j<n; ++j)
|
|
y[j] = x0[j] + x1[j];
|
|
}
|
|
else
|
|
{
|
|
for(j=0; j<n; ++j)
|
|
y[j] = x0[j] * x1[j] * gain;
|
|
}
|
|
|
|
x0 = y;
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRingModRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return cmDspSetEvent(ctx,inst,evt);
|
|
}
|
|
|
|
cmDspClass_t* cmRingModClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmRingModDC,ctx,"RingMod",
|
|
NULL,
|
|
_cmDspRingModAlloc,
|
|
NULL,
|
|
_cmDspRingModReset,
|
|
_cmDspRingModExec,
|
|
_cmDspRingModRecv,
|
|
NULL,NULL,
|
|
"Ring modulator");
|
|
|
|
return &_cmRingModDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspMsgDelay file_desc:"Delay an arbitrary value." kw:[sunit] }
|
|
enum
|
|
{
|
|
kMaxCntMdId,
|
|
kDelayMdId,
|
|
kClearMdId,
|
|
kInMdId,
|
|
kOutMdId,
|
|
};
|
|
|
|
cmDspClass_t _cmMsgDelayDC;
|
|
|
|
typedef struct cmDspMsgDelayEle_str
|
|
{
|
|
unsigned outTimeSmp;
|
|
cmDspValue_t value;
|
|
struct cmDspMsgDelayEle_str* link;
|
|
} cmDspMsgDelayEle_t;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned maxCnt;
|
|
cmDspMsgDelayEle_t* array; // array[maxCnt];
|
|
cmDspMsgDelayEle_t* avail;
|
|
cmDspMsgDelayEle_t* active;
|
|
} cmDspMsgDelay_t;
|
|
|
|
cmDspInst_t* _cmDspMsgDelayAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspMsgDelay_t* p = cmDspInstAllocV(cmDspMsgDelay_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "maxcnt", kMaxCntMdId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Maximum count of elements in the delay",
|
|
1, "delay", kDelayMdId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Delay time in millisecond.",
|
|
1, "clear", kClearMdId, 0, 0, kInDsvFl | kTypeDsvMask, "Clear delay",
|
|
1, "in", kInMdId, 0, 0, kInDsvFl | kUIntDsvFl, "Msg input",
|
|
1, "out", kOutMdId, 0, 0, kOutDsvFl | kUIntDsvFl, "Msg output",
|
|
0 );
|
|
|
|
if( p == NULL )
|
|
return NULL;
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kDelayMdId, 0.0, 0.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kOutMdId, false, false );
|
|
|
|
p->maxCnt = cmDspUInt(&p->inst,kMaxCntMdId);
|
|
p->array = cmMemAllocZ(cmDspMsgDelayEle_t, p->maxCnt );
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMsgDelayFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspMsgDelay_t* p = (cmDspMsgDelay_t*)inst;
|
|
cmMemFree(p->array);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
// insert a value in the delay list
|
|
cmDspRC_t _cmDspMsgDelayInsert( cmDspCtx_t* ctx, cmDspMsgDelay_t* p, unsigned delayTimeSmp, const cmDspValue_t* valPtr )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
// protect against pre-reset calls
|
|
if( p->avail == NULL && p->active == NULL )
|
|
return kOkDspRC;
|
|
|
|
// if there are no available delay elements
|
|
if( p->avail == NULL )
|
|
return cmDspInstErr(ctx,&p->inst,kInvalidStateDspRC,"The message delay has exhausted it's internal message store.");
|
|
|
|
// we only do the simplest kind of copying to avoid allocating memory
|
|
// TODO: fix this
|
|
if( cmDsvIsMtx(valPtr) || cmIsFlag(valPtr->flags,kProxyDsvFl))
|
|
return cmDspInstErr(ctx,&p->inst,kInvalidArgDspRC,"The message delay cannot yet store matrix or proxy types.");
|
|
|
|
// get a pointer to the next available element
|
|
cmDspMsgDelayEle_t* np = p->avail;
|
|
|
|
// remove the new ele from the avail list
|
|
p->avail = np->link;
|
|
|
|
// calc the new ele's exec time
|
|
np->outTimeSmp = ctx->cycleCnt * cmDspSamplesPerCycle(ctx) + delayTimeSmp;
|
|
|
|
// copy the msg value into the delay line element
|
|
// TODO: this should be a real copy that supports all types
|
|
np->value = *valPtr;
|
|
|
|
cmDspMsgDelayEle_t* ep = p->active;
|
|
cmDspMsgDelayEle_t* pp = NULL;
|
|
|
|
// if the active list is empty ...
|
|
if( ep == NULL )
|
|
{
|
|
// ... make the avail element the first on the list
|
|
p->active = np;
|
|
np->link = NULL;
|
|
}
|
|
else
|
|
{
|
|
// iterate through the list and find the active links which
|
|
// the new ele falls between based on its execution time
|
|
while(ep != NULL )
|
|
{
|
|
// ep's exec time is greater than the new ele's exec time
|
|
if( ep->outTimeSmp > np->outTimeSmp )
|
|
{
|
|
|
|
// insert the new ele in the active list before 'ep'
|
|
if( pp == NULL )
|
|
{
|
|
np->link = p->active;
|
|
p->active = np;
|
|
}
|
|
else
|
|
{
|
|
np->link = pp->link;
|
|
pp->link = np;
|
|
}
|
|
break;
|
|
}
|
|
|
|
pp = ep;
|
|
ep = ep->link;
|
|
}
|
|
|
|
// if the new element is last on the list
|
|
if( ep == NULL )
|
|
{
|
|
assert(pp != NULL && pp->link == NULL);
|
|
pp->link = np;
|
|
np->link = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
void _cmDspMsgDelayClear(cmDspInst_t* inst )
|
|
{
|
|
unsigned i;
|
|
cmDspMsgDelay_t* p = (cmDspMsgDelay_t*)inst;
|
|
unsigned maxCnt = cmDspUInt(inst,kMaxCntMdId);
|
|
p->active = NULL;
|
|
p->avail = NULL;
|
|
|
|
// put all ele's on the available list
|
|
for(i=0; i<maxCnt; ++i)
|
|
{
|
|
p->array[i].link = p->avail;
|
|
p->avail = p->array + i;
|
|
}
|
|
}
|
|
|
|
cmDspRC_t _cmDspMsgDelayReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyDefault(ctx,inst,kDelayMdId)) == kOkDspRC )
|
|
{
|
|
_cmDspMsgDelayClear(inst);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMsgDelayExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* e )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspMsgDelay_t* p = (cmDspMsgDelay_t*)inst;
|
|
unsigned framesPerCycle = cmDspSamplesPerCycle(ctx);
|
|
unsigned begTimeSmp = ctx->cycleCnt * framesPerCycle;
|
|
unsigned endTimeSmp = begTimeSmp + framesPerCycle;
|
|
|
|
while( p->active != NULL )
|
|
{
|
|
|
|
if( p->active->outTimeSmp >= endTimeSmp )
|
|
break;
|
|
|
|
cmDspMsgDelayEle_t* ep = p->active;
|
|
|
|
// remove the element from the active list and place it on the available list.
|
|
p->active = p->active->link; // advance the active list
|
|
ep->link = p->avail; // put the cur. element on the avail list
|
|
p->avail = ep; //
|
|
|
|
// output the element value
|
|
if((rc = cmDspValueSet(ctx,inst,kOutMdId,&ep->value,0)) != kOkDspRC )
|
|
return cmDspInstErr(ctx,inst,rc,"Message delay output failed.");
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspMsgDelayRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspMsgDelay_t* p = (cmDspMsgDelay_t*)inst;
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kDelayMdId:
|
|
rc = cmDspSetEvent(ctx,inst,evt);
|
|
break;
|
|
|
|
case kClearMdId:
|
|
_cmDspMsgDelayClear(inst);
|
|
break;
|
|
|
|
case kInMdId:
|
|
{
|
|
unsigned delayTimeSmp = floor(cmDspDouble(&p->inst,kDelayMdId) * cmDspSampleRate(ctx) / 1000.0);
|
|
rc = _cmDspMsgDelayInsert(ctx,p,delayTimeSmp,evt->valuePtr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmMsgDelayClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmMsgDelayDC,ctx,"MsgDelay",
|
|
NULL,
|
|
_cmDspMsgDelayAlloc,
|
|
_cmDspMsgDelayFree,
|
|
_cmDspMsgDelayReset,
|
|
_cmDspMsgDelayExec,
|
|
_cmDspMsgDelayRecv,
|
|
NULL,NULL,
|
|
"Message Delay");
|
|
|
|
return &_cmMsgDelayDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspLine file_desc:"Programmable line generator." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBegLnId,
|
|
kEndLnId,
|
|
kDurLnId,
|
|
kCmdLnId,
|
|
kRateLnId,
|
|
kOutLnId,
|
|
};
|
|
|
|
cmDspClass_t _cmLineDC;
|
|
|
|
typedef struct cmDspLineEle_str
|
|
{
|
|
unsigned outTimeSmp;
|
|
cmDspValue_t value;
|
|
struct cmDspLineEle_str* link;
|
|
} cmDspLineEle_t;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned onSymId;
|
|
unsigned offSymId;
|
|
unsigned resetSymId;
|
|
unsigned curSmpIdx;
|
|
double phase;
|
|
bool onFl;
|
|
} cmDspLine_t;
|
|
|
|
cmDspInst_t* _cmDspLineAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspLine_t* p = cmDspInstAllocV(cmDspLine_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "beg", kBegLnId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Begin value.",
|
|
1, "end", kEndLnId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "End value.",
|
|
1, "dur", kDurLnId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Duration (ms)",
|
|
1, "cmd", kCmdLnId, 0, 0, kInDsvFl | kSymDsvFl | kOptArgDsvFl, "Command: on | off | reset",
|
|
1, "rate", kRateLnId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output messages per second",
|
|
1, "out", kOutLnId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Output",
|
|
0 );
|
|
|
|
if( p == NULL )
|
|
return NULL;
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutLnId, 0.0, cmDspDefaultDouble(&p->inst,kBegLnId) );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kRateLnId, 0.0, 0.0 );
|
|
|
|
p->onSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"on");
|
|
p->offSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"off");
|
|
p->resetSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"reset");
|
|
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspLineFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return kOkDspRC;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspLineReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspLine_t* p = (cmDspLine_t*)inst;
|
|
p->curSmpIdx = 0;
|
|
p->onFl = false;
|
|
p->phase = 0;
|
|
return cmDspApplyAllDefaults(ctx,inst);
|
|
}
|
|
|
|
cmDspRC_t _cmDspLineExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* e )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspLine_t* p = (cmDspLine_t*)inst;
|
|
|
|
if( p->onFl == false )
|
|
return kOkDspRC;
|
|
|
|
unsigned sPc = cmDspSamplesPerCycle(ctx);
|
|
double beg = cmDspDouble(inst,kBegLnId);
|
|
double end = cmDspDouble(inst,kEndLnId);
|
|
double rate = cmDspDouble(inst,kRateLnId);
|
|
double ms = cmDspDouble(inst,kDurLnId);
|
|
double durSmpCnt = floor(ms * cmDspSampleRate(ctx) / 1000);
|
|
double out = beg + (end - beg) * p->curSmpIdx / durSmpCnt;
|
|
double phsMax = rate==0 ? sPc : cmDspSampleRate(ctx) / rate;
|
|
|
|
// we can never output with a period shorter than
|
|
// the length of one audio buffer
|
|
if( phsMax < sPc )
|
|
phsMax = sPc;
|
|
|
|
|
|
if( beg < end )
|
|
{
|
|
if( out >= end )
|
|
{
|
|
out = end;
|
|
p->onFl = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( out <= end )
|
|
{
|
|
out = end;
|
|
p->onFl = false;
|
|
}
|
|
}
|
|
|
|
p->phase += sPc;
|
|
|
|
if( p->phase >= sPc )
|
|
{
|
|
cmDspSetDouble(ctx,inst,kOutLnId,out);
|
|
p->phase -= sPc;
|
|
}
|
|
|
|
p->curSmpIdx += sPc;
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspLineRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspLine_t* p = (cmDspLine_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kCmdLnId:
|
|
{
|
|
unsigned symId = cmDspSymbol(inst,kCmdLnId);
|
|
|
|
if( symId == p->onSymId )
|
|
p->onFl = true;
|
|
else
|
|
if( symId == p->offSymId )
|
|
p->onFl = false;
|
|
else
|
|
if( symId == p->resetSymId )
|
|
{
|
|
p->curSmpIdx = 0;
|
|
p->onFl = true;
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmLineClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmLineDC,ctx,"Line",
|
|
NULL,
|
|
_cmDspLineAlloc,
|
|
_cmDspLineFree,
|
|
_cmDspLineReset,
|
|
_cmDspLineExec,
|
|
_cmDspLineRecv,
|
|
NULL,NULL,
|
|
"Line");
|
|
|
|
return &_cmLineDC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspAdsr file_desc:"ADSR envelope generator." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kTrigModeAdId,
|
|
kMinLvlAdId,
|
|
kDlyMsAdId,
|
|
kAtkMsAdId,
|
|
kAtkLvlAdId,
|
|
kDcyMsAdId,
|
|
kSusLvlAdId,
|
|
kSusMsAdId,
|
|
kRlsMsAdId,
|
|
kTScaleAdId,
|
|
kAScaleAdId,
|
|
kGateAdId,
|
|
kRmsAdId,
|
|
kOutAdId,
|
|
kCmdAdId
|
|
};
|
|
|
|
cmDspClass_t _cmAdsrDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmAdsr* p;
|
|
} cmDspAdsr_t;
|
|
|
|
cmDspInst_t* _cmDspAdsrAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspAdsr_t* p = cmDspInstAllocV(cmDspAdsr_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "trig", kTrigModeAdId, 0, 0, kBoolDsvFl | kInDsvFl | kOptArgDsvFl, "Trigger mode (offset ignored).",
|
|
1, "min", kMinLvlAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Minimum level (dflt:0.0).",
|
|
1, "dly", kDlyMsAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Delay milliseconds.",
|
|
1, "atk", kAtkMsAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Attack milliseconds.",
|
|
1, "alvl", kAtkLvlAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Attack Level.",
|
|
1, "dcy", kDcyMsAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Decay milliseconds.",
|
|
1, "sus", kSusLvlAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Sustain Level.",
|
|
1, "hold", kSusMsAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Sustain Ms (trig mode only).",
|
|
1, "rls", kRlsMsAdId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl, "Release milliseconds.",
|
|
1, "tscale",kTScaleAdId, 0, 0, kDoubleDsvFl | kInDsvFl, "Time scale.",
|
|
1, "ascale",kAScaleAdId, 0, 0, kDoubleDsvFl | kInDsvFl, "Amplitude scale.",
|
|
1, "gate", kGateAdId, 0, 0, kBoolDsvFl | kInDsvFl, "Gate input.",
|
|
1, "rms", kRmsAdId, 0, 0, kDoubleDsvFl | kInDsvFl, "RMS input.",
|
|
1, "out", kOutAdId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Level output.",
|
|
1, "cmd", kCmdAdId, 0, 0, kSymDsvFl | kInDsvFl, "Command input.",
|
|
0 );
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kTrigModeAdId, false, false );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kMinLvlAdId, 0.0, 0.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kDlyMsAdId, 0.0, 0.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kAtkMsAdId, 0.0, 5.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kAtkLvlAdId, 0.0, 1.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kDcyMsAdId, 0.0, 10.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kSusLvlAdId, 0.0, 0.8 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kSusMsAdId, 0.0, 50.0);
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kRlsMsAdId, 0.0, 20.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kTScaleAdId, 0.0, 1.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kAScaleAdId, 0.0, 1.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kGateAdId, false,false);
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kRmsAdId, 0.0, 0.0);
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutAdId, 0.0, cmDspDouble(&p->inst,kMinLvlAdId));
|
|
|
|
p->p = cmAdsrAlloc(ctx->cmProcCtx,NULL,false,0,0,0,0,0,0,0,0,0);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAdsrFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspAdsr_t* p = (cmDspAdsr_t*)inst;
|
|
cmAdsrFree(&p->p);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAdsrReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAdsr_t* p = (cmDspAdsr_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
bool trigFl= cmDspBool( inst, kTrigModeAdId );
|
|
cmReal_t minL = cmDspDouble( inst, kMinLvlAdId );
|
|
cmReal_t dlyMs = cmDspDouble( inst, kDlyMsAdId );
|
|
cmReal_t atkMs = cmDspDouble( inst, kAtkMsAdId );
|
|
cmReal_t atkL = cmDspDouble( inst, kAtkLvlAdId );
|
|
cmReal_t dcyMs = cmDspDouble( inst, kDcyMsAdId );
|
|
cmReal_t susMs = cmDspDouble( inst, kSusMsAdId );
|
|
cmReal_t susL = cmDspDouble( inst, kSusLvlAdId );
|
|
cmReal_t rlsMs = cmDspDouble( inst, kRlsMsAdId );
|
|
|
|
cmAdsrInit( p->p, cmDspSampleRate(ctx), trigFl, minL, dlyMs, atkMs, atkL, dcyMs, susMs, susL, rlsMs );
|
|
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAdsrExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAdsr_t* p = (cmDspAdsr_t*)inst;
|
|
bool gateFl = cmDspBool( inst, kGateAdId );
|
|
//double rms = cmDspDouble(inst,kRmsAdId);
|
|
double tscale = cmDspDouble(inst,kTScaleAdId);
|
|
double ascale = cmDspDouble(inst,kAScaleAdId);
|
|
|
|
//
|
|
// HACK HACK HACK HACK
|
|
// HACK HACK HACK HACK
|
|
// HACK HACK HACK HACK see the accompanying hack in cmProc3.c cmAdsrExec()
|
|
// HACK HACK HACK HACK
|
|
// HACK HACK HACK HACK
|
|
//
|
|
|
|
// double db = rms<0.00001 ? -100.0 : 20.0*log10(rms);
|
|
// double dbMax = -15.0;
|
|
// double dbMin = -58.0;
|
|
//
|
|
// db = cmMin(dbMax,cmMax(dbMin,db));
|
|
// double scale = (db - dbMin) / (dbMax-dbMin);
|
|
|
|
cmReal_t out = cmAdsrExec( p->p, cmDspSamplesPerCycle(ctx), gateFl, tscale, ascale );
|
|
|
|
rc = cmDspSetDouble( ctx, inst, kOutAdId, out );
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAdsrRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAdsr_t* p = (cmDspAdsr_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
if( evt->dstVarId == kCmdAdId )
|
|
{
|
|
cmAdsrReport(p->p,ctx->rpt);
|
|
return rc;
|
|
}
|
|
|
|
cmReal_t v = cmDspDouble(inst,evt->dstVarId);
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kTrigModeAdId:
|
|
p->p->trigModeFl = cmDspBool(inst, kTrigModeAdId);
|
|
break;
|
|
|
|
case kMinLvlAdId:
|
|
cmAdsrSetLevel(p->p, v, kDlyAdsrId );
|
|
break;
|
|
|
|
case kDlyMsAdId:
|
|
cmAdsrSetTime(p->p, v, kDlyAdsrId );
|
|
break;
|
|
|
|
case kAtkMsAdId:
|
|
cmAdsrSetTime(p->p, v, kAtkAdsrId );
|
|
break;
|
|
|
|
case kAtkLvlAdId:
|
|
cmAdsrSetLevel(p->p, v, kAtkAdsrId );
|
|
break;
|
|
|
|
case kDcyMsAdId:
|
|
cmAdsrSetTime(p->p, v, kDcyAdsrId );
|
|
break;
|
|
|
|
case kSusMsAdId:
|
|
cmAdsrSetTime(p->p, v, kSusAdsrId );
|
|
break;
|
|
|
|
case kSusLvlAdId:
|
|
cmAdsrSetLevel(p->p, v, kSusAdsrId );
|
|
break;
|
|
|
|
case kRlsMsAdId:
|
|
cmAdsrSetTime(p->p, v, kRlsAdsrId );
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmAdsrClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmAdsrDC,ctx,"Adsr",
|
|
NULL,
|
|
_cmDspAdsrAlloc,
|
|
_cmDspAdsrFree,
|
|
_cmDspAdsrReset,
|
|
_cmDspAdsrExec,
|
|
_cmDspAdsrRecv,
|
|
NULL,NULL,
|
|
"ADSR Envelope Generator");
|
|
|
|
return &_cmAdsrDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspAdsr file_desc:"Audio dynamics compressor." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassCmId,
|
|
kThreshDbCmId,
|
|
kRatioCmId,
|
|
kAtkMsCmId,
|
|
kRlsMsCmId,
|
|
kInGainCmId,
|
|
kOutGainCmId,
|
|
kWndMsCmId,
|
|
kMaxWndMsCmId,
|
|
kInCmId,
|
|
kOutCmId,
|
|
kEnvCmId
|
|
};
|
|
|
|
cmDspClass_t _cmCompressorDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmCompressor* p;
|
|
} cmDspCompressor_t;
|
|
|
|
cmDspInst_t* _cmDspCompressorAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspCompressor_t* p = cmDspInstAllocV(cmDspCompressor_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "bypass",kBypassCmId, 0, 0, kInDsvFl | kBoolDsvFl | kReqArgDsvFl, "Bypass enable",
|
|
1, "thr", kThreshDbCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Threshold in dB.",
|
|
1, "ratio", kRatioCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Ratio numerator.",
|
|
1, "atk", kAtkMsCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Attack milliseconds",
|
|
1, "rls", kRlsMsCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Release milliseconds",
|
|
1, "igain", kInGainCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Input gain.",
|
|
1, "ogain", kOutGainCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Makeup Gain",
|
|
1, "wnd", kWndMsCmId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "RMS window milliseconds.",
|
|
1, "maxwnd",kMaxWndMsCmId, 0, 0, kDoubleDsvFl | kOptArgDsvFl, "Max. RMS window milliseconds.",
|
|
1, "in", kInCmId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input",
|
|
1, "out", kOutCmId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output",
|
|
1, "env", kEnvCmId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Envelope out",
|
|
0 );
|
|
|
|
p->p = cmCompressorAlloc(ctx->cmProcCtx, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false );
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassCmId, false, false );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kAtkMsCmId, 0.0, 20.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kRlsMsCmId, 0.0, 20.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kInGainCmId, 0.0, 1.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutGainCmId, 0.0, 1.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kWndMsCmId, 0.0, 200.0);
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kMaxWndMsCmId, 0.0, 1000.0);
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kEnvCmId, 0.0, 0.0 );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCompressorFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspCompressor_t* p = (cmDspCompressor_t*)inst;
|
|
cmCompressorFree(&p->p);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCompressorReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspCompressor_t* p = (cmDspCompressor_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
cmDspZeroAudioBuf(ctx,inst,kOutCmId);
|
|
|
|
|
|
cmReal_t threshDb = cmDspDouble(inst, kThreshDbCmId );
|
|
cmReal_t ratio = cmDspDouble(inst, kRatioCmId );
|
|
cmReal_t atkMs = cmDspDouble(inst, kAtkMsCmId );
|
|
cmReal_t rlsMs = cmDspDouble(inst, kRlsMsCmId );
|
|
cmReal_t inGain = cmDspDouble(inst, kInGainCmId );
|
|
cmReal_t outGain = cmDspDouble(inst, kOutGainCmId );
|
|
cmReal_t wndMs = cmDspDouble(inst, kWndMsCmId );
|
|
cmReal_t maxWndMs = cmDspDouble(inst, kMaxWndMsCmId);
|
|
bool bypassFl = cmDspBool( inst, kBypassCmId );
|
|
cmCompressorInit(p->p,cmDspSampleRate(ctx),cmDspSamplesPerCycle(ctx), inGain, maxWndMs, wndMs, threshDb, ratio, atkMs, rlsMs, outGain, bypassFl );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCompressorExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspCompressor_t* p = (cmDspCompressor_t*)inst;
|
|
cmSample_t* y = cmDspAudioBuf(ctx,inst,kOutCmId, 0);
|
|
const cmSample_t* x = cmDspAudioBuf(ctx,inst,kInCmId,0);
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutCmId,0);
|
|
|
|
if( x != NULL )
|
|
{
|
|
cmCompressorExec(p->p,x,y,n);
|
|
|
|
rc = cmDspSetDouble( ctx, inst, kEnvCmId, p->p->gain );
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspCompressorRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
cmDspCompressor_t* p = (cmDspCompressor_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
cmReal_t v = cmDspDouble(inst,evt->dstVarId);
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kThreshDbCmId:
|
|
cmCompressorSetThreshDb(p->p,v);
|
|
break;
|
|
|
|
case kRatioCmId:
|
|
p->p->ratio_num = v;
|
|
break;
|
|
|
|
case kAtkMsCmId:
|
|
cmCompressorSetAttackMs(p->p,v);
|
|
break;
|
|
|
|
case kRlsMsCmId:
|
|
cmCompressorSetReleaseMs(p->p,v);
|
|
break;
|
|
|
|
case kInGainCmId:
|
|
p->p->inGain = v;
|
|
break;
|
|
|
|
case kOutGainCmId:
|
|
p->p->outGain = v;
|
|
break;
|
|
|
|
case kWndMsCmId:
|
|
cmCompressorSetRmsWndMs(p->p,v);
|
|
break;
|
|
|
|
case kBypassCmId:
|
|
p->p->bypassFl = cmDspBool(inst,kBypassCmId);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmCompressorClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmCompressorDC,ctx,"Compressor",
|
|
NULL,
|
|
_cmDspCompressorAlloc,
|
|
_cmDspCompressorFree,
|
|
_cmDspCompressorReset,
|
|
_cmDspCompressorExec,
|
|
_cmDspCompressorRecv,
|
|
NULL,NULL,
|
|
"Compressor");
|
|
|
|
return &_cmCompressorDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspBiquad file_desc:"Programmable Biquad EQ filter." kw:[sunit] }
|
|
enum
|
|
{
|
|
kBypassBqId,
|
|
kModeBqId,
|
|
kF0HzBqId,
|
|
kQBqId,
|
|
kGainDbBqId,
|
|
kInBqId,
|
|
kOutBqId
|
|
};
|
|
|
|
cmDspClass_t _cmBiQuadEqDC;
|
|
|
|
typedef struct
|
|
{
|
|
const cmChar_t* label;
|
|
unsigned mode;
|
|
unsigned symbol;
|
|
} cmDspBiQuadMap_t;
|
|
|
|
cmDspBiQuadMap_t _cmDspBiQuadMap[] =
|
|
{
|
|
{"LP", kLpfBqId, cmInvalidId },
|
|
{"HP", kHpFBqId, cmInvalidId },
|
|
{"BP", kBpfBqId, cmInvalidId },
|
|
{"Notch", kNotchBqId, cmInvalidId },
|
|
{"AP", kAllpassBqId, cmInvalidId },
|
|
{"Peak", kPeakBqId, cmInvalidId },
|
|
{"LSh", kLowShelfBqId, cmInvalidId },
|
|
{"HSh", kHighShelfBqId, cmInvalidId },
|
|
{ NULL, cmInvalidId, cmInvalidId }
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmBiQuadEq* p;
|
|
} cmDspBiQuadEq_t;
|
|
|
|
cmDspInst_t* _cmDspBiQuadEqAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspBiQuadEq_t* p = cmDspInstAllocV(cmDspBiQuadEq_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "bypass",kBypassBqId, 0, 0, kInDsvFl | kBoolDsvFl | kReqArgDsvFl, "Bypass enable.",
|
|
1, "mode", kModeBqId, 0, 0, kInDsvFl | kSymDsvFl | kReqArgDsvFl, "Mode Symbol: LP|HP|BP|AP|Notch|Pk|LSh|HSh.",
|
|
1, "f0", kF0HzBqId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Center or edge frequecy in Hz.",
|
|
1, "Q", kQBqId, 0, 0, kInDsvFl | kDoubleDsvFl | kReqArgDsvFl, "Q",
|
|
1, "gain", kGainDbBqId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Gain Db (Pk,LSh,Hsh only)",
|
|
1, "in", kInBqId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input",
|
|
1, "out", kOutBqId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output",
|
|
0 );
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kGainDbBqId, 0.0, 1.0 );
|
|
|
|
p->p = cmBiQuadEqAlloc(ctx->cmProcCtx,NULL,0,0,0,0,0,false);
|
|
|
|
// register the filter mode symbols
|
|
unsigned i;
|
|
for(i=0; _cmDspBiQuadMap[i].label != NULL; ++i)
|
|
if( _cmDspBiQuadMap[i].symbol == cmInvalidId )
|
|
_cmDspBiQuadMap[i].symbol = cmSymTblRegisterStaticSymbol(ctx->stH,_cmDspBiQuadMap[i].label);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspBiQuadEqFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspBiQuadEq_t* p = (cmDspBiQuadEq_t*)inst;
|
|
cmBiQuadEqFree(&p->p);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
unsigned _cmDspBiQuadEqModeId( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned modeSymId )
|
|
{
|
|
unsigned i;
|
|
|
|
for(i=0; _cmDspBiQuadMap[i].label!=NULL; ++i)
|
|
if( _cmDspBiQuadMap[i].symbol == modeSymId )
|
|
return _cmDspBiQuadMap[i].mode;
|
|
|
|
cmDspInstErr(ctx,inst,kVarNotValidDspRC,"The mode string '%s' is not valid.",cmStringNullGuard(cmSymTblLabel(ctx->stH,modeSymId)));
|
|
|
|
return cmInvalidId;
|
|
}
|
|
|
|
cmDspRC_t _cmDspBiQuadEqReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspBiQuadEq_t* p = (cmDspBiQuadEq_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
cmDspZeroAudioBuf(ctx,inst,kOutBqId);
|
|
|
|
unsigned modeSymId = cmDspSymbol(inst, kModeBqId );
|
|
cmReal_t f0Hz = cmDspDouble(inst, kF0HzBqId );
|
|
cmReal_t Q = cmDspDouble(inst, kQBqId );
|
|
cmReal_t gainDb = cmDspDouble(inst, kGainDbBqId );
|
|
unsigned mode = _cmDspBiQuadEqModeId(ctx,inst,modeSymId );
|
|
bool bypassFl = cmDspBool(inst, kBypassBqId );
|
|
|
|
if( mode != cmInvalidId )
|
|
cmBiQuadEqInit(p->p, cmDspSampleRate(ctx), mode, f0Hz, Q, gainDb, bypassFl );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspBiQuadEqExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspBiQuadEq_t* p = (cmDspBiQuadEq_t*)inst;
|
|
cmSample_t* y = cmDspAudioBuf(ctx,inst,kOutBqId, 0);
|
|
const cmSample_t* x = cmDspAudioBuf(ctx,inst,kInBqId,0);
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutBqId,0);
|
|
|
|
cmBiQuadEqExec(p->p,x,y,n);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspBiQuadEqRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
cmDspBiQuadEq_t* p = (cmDspBiQuadEq_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
cmReal_t f0Hz = p->p->f0Hz;
|
|
cmReal_t Q = p->p->Q;
|
|
cmReal_t gainDb = p->p->gainDb;
|
|
unsigned mode = p->p->mode;
|
|
|
|
if( evt->dstVarId == kModeBqId )
|
|
{
|
|
if((mode = _cmDspBiQuadEqModeId(ctx,inst,cmDspSymbol(inst,kModeBqId) )) == cmInvalidId )
|
|
rc = kVarNotValidDspRC;
|
|
}
|
|
else
|
|
{
|
|
cmReal_t v = cmDspDouble(inst,evt->dstVarId);
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kF0HzBqId:
|
|
f0Hz = v;
|
|
break;
|
|
|
|
case kQBqId:
|
|
Q = v;
|
|
break;
|
|
|
|
case kGainDbBqId:
|
|
gainDb = v;
|
|
break;
|
|
|
|
case kBypassBqId:
|
|
p->p->bypassFl = cmDspBool(inst,kBypassBqId);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
cmBiQuadEqSet(p->p,mode,f0Hz,Q,gainDb);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmBiQuadEqClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmBiQuadEqDC,ctx,"BiQuadEq",
|
|
NULL,
|
|
_cmDspBiQuadEqAlloc,
|
|
_cmDspBiQuadEqFree,
|
|
_cmDspBiQuadEqReset,
|
|
_cmDspBiQuadEqExec,
|
|
_cmDspBiQuadEqRecv,
|
|
NULL,NULL,
|
|
"Bi-Quad EQ Filters");
|
|
|
|
return &_cmBiQuadEqDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspDistDs file_desc:"Guitar style distortion effect." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kBypassDsId,
|
|
kInGainDsId,
|
|
kSrateDsId,
|
|
kBitsDsId,
|
|
kRectDsId,
|
|
kFullDsId,
|
|
kClipDbDsId,
|
|
kOutGainDsId,
|
|
kInDsId,
|
|
kOutDsId
|
|
};
|
|
|
|
cmDspClass_t _cmDistDsDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
cmDistDs* p;
|
|
} cmDspDistDs_t;
|
|
|
|
cmDspInst_t* _cmDspDistDsAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
|
|
cmDspDistDs_t* p = cmDspInstAllocV(cmDspDistDs_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "bypass", kBypassDsId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Bypass enable.",
|
|
1, "igain", kInGainDsId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Input gain.",
|
|
1, "srate", kSrateDsId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Down-sample rate.",
|
|
1, "bits", kBitsDsId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Bits per sample",
|
|
1, "rect", kRectDsId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "Rectify flag",
|
|
1, "full", kFullDsId, 0, 0, kInDsvFl | kBoolDsvFl | kOptArgDsvFl, "1=Full 0=Half rectify",
|
|
1, "clip", kClipDbDsId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Clip dB",
|
|
1, "ogain", kOutGainDsId, 0, 0, kInDsvFl | kDoubleDsvFl | kOptArgDsvFl, "Output gain",
|
|
1, "in", kInDsId, 0, 0, kInDsvFl | kAudioBufDsvFl, "Audio input",
|
|
1, "out", kOutDsId, 0, 1, kOutDsvFl | kAudioBufDsvFl, "Audio output",
|
|
0 );
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBypassDsId, false, false );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kInGainDsId, 0.0, 1.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kSrateDsId, 0.0, cmDspSampleRate(ctx));
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kBitsDsId, 0.0, 24.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kRectDsId, false, false );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kFullDsId, false, false );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kClipDbDsId, 0.0, 0.0 );
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutGainDsId, 0.0, 1.0 );
|
|
|
|
p->p = cmDistDsAlloc(ctx->cmProcCtx, NULL, 0, 0, 0, 0, false, false, 0, 0, false );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDistDsFree(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspDistDs_t* p = (cmDspDistDs_t*)inst;
|
|
cmDistDsFree(&p->p);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDistDsReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspDistDs_t* p = (cmDspDistDs_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
cmDspZeroAudioBuf(ctx,inst,kOutDsId);
|
|
|
|
bool bypassFl = cmDspBool( inst, kBypassDsId );
|
|
cmReal_t inGain = cmDspDouble( inst, kInGainDsId );
|
|
cmReal_t downSrate = cmDspDouble( inst, kSrateDsId );
|
|
cmReal_t bits = cmDspDouble( inst, kBitsDsId );
|
|
bool rectFl = cmDspBool( inst, kRectDsId );
|
|
bool fullFl = cmDspBool( inst, kFullDsId );
|
|
cmReal_t clipDb = cmDspDouble( inst, kClipDbDsId );
|
|
cmReal_t outGain = cmDspDouble( inst, kOutGainDsId );
|
|
cmDistDsInit(p->p, cmDspSampleRate(ctx), inGain, downSrate, bits, rectFl, fullFl, clipDb, outGain, bypassFl );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDistDsExec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspDistDs_t* p = (cmDspDistDs_t*)inst;
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutDsId,0);
|
|
cmSample_t* y = cmDspAudioBuf(ctx,inst,kOutDsId,0);
|
|
const cmSample_t* x = cmDspAudioBuf(ctx,inst,kInDsId,0);
|
|
|
|
cmDistDsExec(p->p,x,y,n);
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspDistDsRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspDistDs_t* p = (cmDspDistDs_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kInGainDsId:
|
|
p->p->inGain = cmDspDouble(inst,kInGainDsId);
|
|
break;
|
|
|
|
case kSrateDsId:
|
|
p->p->downSrate = cmDspDouble(inst,kSrateDsId);
|
|
break;
|
|
|
|
case kBitsDsId:
|
|
p->p->bits = cmDspDouble(inst,kBitsDsId);
|
|
break;
|
|
|
|
case kRectDsId:
|
|
p->p->rectFl = cmDspBool(inst,kRectDsId);
|
|
break;
|
|
|
|
case kFullDsId:
|
|
p->p->fullFl = cmDspBool(inst,kFullDsId);
|
|
break;
|
|
|
|
case kClipDbDsId:
|
|
p->p->clipDb = cmDspDouble(inst,kClipDbDsId);
|
|
break;
|
|
|
|
case kOutGainDsId:
|
|
p->p->outGain = cmDspDouble(inst,kOutGainDsId);
|
|
break;
|
|
|
|
case kBypassDsId:
|
|
p->p->bypassFl = cmDspBool(inst,kBypassDsId);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmDistDsClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmDistDsDC,ctx,"DistDs",
|
|
NULL,
|
|
_cmDspDistDsAlloc,
|
|
_cmDspDistDsFree,
|
|
_cmDspDistDsReset,
|
|
_cmDspDistDsExec,
|
|
_cmDspDistDsRecv,
|
|
NULL,NULL,
|
|
"Distortion and Downsampler");
|
|
|
|
return &_cmDistDsDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspDbToLin file_desc:"Convert decibel units to linear units." kw:[sunit] }
|
|
enum
|
|
{
|
|
kInDlId,
|
|
kOutDlId
|
|
};
|
|
|
|
cmDspClass_t _cmDbToLinDC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
} cmDspDbToLin_t;
|
|
|
|
cmDspInst_t* _cmDspDbToLinAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
|
|
cmDspDbToLin_t* p = cmDspInstAllocV(cmDspDbToLin_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "in", kInDlId, 0, 0, kInDsvFl | kDoubleDsvFl, "Input",
|
|
1, "out", kOutDlId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Output",
|
|
0 );
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kInDlId, 0.0, -1000.0);
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutDlId, 0.0, 0.0 );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspDbToLinReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return cmDspApplyAllDefaults(ctx,inst);
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspDbToLinRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
if( evt->dstVarId == kInDlId )
|
|
{
|
|
double db = cmMax(0.0,cmMin(100.0,cmDspDouble(inst,kInDlId)));
|
|
double lin = db==0 ? 0.0 : pow(10.0, (db-100.0)/20.0);
|
|
cmDspSetDouble(ctx,inst,kOutDlId,lin);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmDbToLinClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmDbToLinDC,ctx,"DbToLin",
|
|
NULL,
|
|
_cmDspDbToLinAlloc,
|
|
NULL,
|
|
_cmDspDbToLinReset,
|
|
NULL,
|
|
_cmDspDbToLinRecv,
|
|
NULL,NULL,
|
|
"dB to Linear converter");
|
|
|
|
return &_cmDbToLinDC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspNofM2 file_desc:"Pass an N of M possible inputs to the output." kw:[sunit] }
|
|
|
|
// Pass any N of M inputs
|
|
enum
|
|
{
|
|
kInChCntNoId,
|
|
kOutChCntNoId,
|
|
kXfadeMsNoId,
|
|
kCmdNoId,
|
|
kSelIdxNoId,
|
|
kBaseGateNoId
|
|
};
|
|
|
|
cmDspClass_t _cmNofM_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
|
|
unsigned iChCnt;
|
|
unsigned oChCnt;
|
|
|
|
unsigned* map; // map[ oChCnt ]
|
|
cmXfader** xf; // xf[ oChCnt ];
|
|
|
|
unsigned cfgSymId;
|
|
unsigned onSymId;
|
|
unsigned offSymId;
|
|
bool verboseFl;
|
|
|
|
unsigned baseBaseInNoId; // first data input port id
|
|
unsigned baseBaseOutNoId; // first data output port id
|
|
|
|
unsigned baseInFloatNoId;
|
|
unsigned baseInBoolNoId;
|
|
unsigned baseInSymNoId;
|
|
unsigned baseInAudioNoId;
|
|
|
|
unsigned baseOutFloatNoId;
|
|
unsigned baseOutBoolNoId;
|
|
unsigned baseOutSymNoId;
|
|
unsigned baseOutAudioNoId;
|
|
|
|
bool printFl;
|
|
|
|
} cmDspNofM_t;
|
|
|
|
cmDspInst_t* _cmDspNofM_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 2 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'NofM' constructor must given input and output channel counts.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int iChCnt = va_arg(vl,int);
|
|
int oChCnt = va_arg(vl,int);
|
|
|
|
if( oChCnt > iChCnt )
|
|
{
|
|
va_end(vl1);
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'NofM' output count must be less than or equal to the input count.");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
unsigned baseInFloatNoId = kBaseGateNoId + iChCnt;
|
|
unsigned baseInBoolNoId = baseInFloatNoId + iChCnt;
|
|
unsigned baseInSymNoId = baseInBoolNoId + iChCnt;
|
|
unsigned baseInAudioNoId = baseInSymNoId + iChCnt;
|
|
unsigned baseOutFloatNoId = baseInAudioNoId + iChCnt;
|
|
unsigned baseOutBoolNoId = baseOutFloatNoId + oChCnt;
|
|
unsigned baseOutSymNoId = baseOutBoolNoId + oChCnt;
|
|
unsigned baseOutAudioNoId = baseOutSymNoId + oChCnt;
|
|
|
|
unsigned i;
|
|
|
|
cmDspNofM_t* p = cmDspInstAllocV(cmDspNofM_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "ichs", kInChCntNoId, 0, 0, kUIntDsvFl | kReqArgDsvFl,"Input channel count.",
|
|
1, "ochs", kOutChCntNoId, 0, 0, kUIntDsvFl | kReqArgDsvFl,"Output channel count.",
|
|
1, "ms", kXfadeMsNoId, 0, 0, kDoubleDsvFl | kInDsvFl | kOptArgDsvFl,"Audio Cross-fade time in milliseconds.",
|
|
1, "cmd", kCmdNoId, 0, 0, kSymDsvFl | kInDsvFl, "Command input.",
|
|
1, "seli", kSelIdxNoId, 0, 0, kUIntDsvFl | kInDsvFl, "Enable gate at index.",
|
|
iChCnt, "sel", kBaseGateNoId, 0, 0, kBoolDsvFl | kInDsvFl, "Selector Gate inputs.",
|
|
iChCnt, "f-in", baseInFloatNoId, 0, 0, kDoubleDsvFl | kInDsvFl, "Float input",
|
|
iChCnt, "b-in", baseInBoolNoId, 0, 0, kBoolDsvFl | kInDsvFl, "Bool input",
|
|
iChCnt, "s-in", baseInSymNoId, 0, 0, kSymDsvFl | kInDsvFl, "Symbol input",
|
|
iChCnt, "a-in", baseInAudioNoId, 0, 0, kAudioBufDsvFl | kInDsvFl, "Audio input",
|
|
oChCnt, "f-out", baseOutFloatNoId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Float output",
|
|
oChCnt, "b-out", baseOutBoolNoId, 0, 0, kBoolDsvFl | kOutDsvFl, "Bool output",
|
|
oChCnt, "s-out", baseOutSymNoId, 0, 0, kSymDsvFl | kOutDsvFl, "Symbol output",
|
|
oChCnt, "a-out", baseOutAudioNoId, 0, 1, kAudioBufDsvFl | kOutDsvFl, "Audio output",
|
|
0 );
|
|
|
|
p->iChCnt = iChCnt;
|
|
p->oChCnt = oChCnt;
|
|
p->map = cmMemAllocZ(unsigned,oChCnt);
|
|
p->xf = cmMemAllocZ(cmXfader*,oChCnt);
|
|
p->cfgSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"cfg");
|
|
p->onSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"on");
|
|
p->offSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"off");
|
|
|
|
p->verboseFl = false;
|
|
|
|
p->baseBaseInNoId = baseInFloatNoId;
|
|
p->baseBaseOutNoId = baseOutFloatNoId;
|
|
|
|
p->baseInFloatNoId = baseInFloatNoId;
|
|
p->baseInBoolNoId = baseInBoolNoId;
|
|
p->baseInSymNoId = baseInSymNoId;
|
|
p->baseInAudioNoId = baseInAudioNoId;
|
|
p->baseOutFloatNoId = baseOutFloatNoId;
|
|
p->baseOutBoolNoId = baseOutBoolNoId;
|
|
p->baseOutSymNoId = baseOutSymNoId;
|
|
p->baseOutAudioNoId = baseOutAudioNoId;
|
|
|
|
for(i=0; i<oChCnt; ++i)
|
|
{
|
|
cmDspSetDefaultDouble( ctx, &p->inst, baseOutFloatNoId + i, 0.0, 0.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, baseOutBoolNoId + i, false, false );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, baseOutSymNoId + i, cmInvalidId );
|
|
p->xf[i] = cmXfaderAlloc( ctx->cmProcCtx, NULL, 0, 0, 0 );
|
|
}
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kXfadeMsNoId, 0.0, 15.0 );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNofM_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspNofM_t* p = (cmDspNofM_t*)inst;
|
|
unsigned i;
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
cmXfaderFree(&p->xf[i]);
|
|
|
|
cmMemFree(p->map);
|
|
cmMemFree(p->xf);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNofM_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspNofM_t* p = (cmDspNofM_t*)inst;
|
|
unsigned i;
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
{
|
|
cmDspZeroAudioBuf(ctx,inst,p->baseOutAudioNoId+i);
|
|
p->map[i] = cmInvalidIdx;
|
|
double xfadeMs = cmDspDouble(inst,kXfadeMsNoId);
|
|
cmXfaderInit(p->xf[i], cmDspSampleRate(ctx), p->iChCnt, xfadeMs );
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNofM_Exec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
cmDspNofM_t* p = (cmDspNofM_t*)inst;
|
|
unsigned i;
|
|
const cmSample_t* x[ p->iChCnt ];
|
|
|
|
for(i=0; i<p->iChCnt; ++i)
|
|
x[i] = cmDspAudioBuf(ctx,inst,p->baseInAudioNoId + i ,0);
|
|
|
|
// for each valid p->map[] element
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
{
|
|
cmSample_t* y = cmDspAudioBuf(ctx,inst,p->baseOutAudioNoId+ i,0);
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,p->baseOutAudioNoId+i,0);
|
|
|
|
if( y != NULL )
|
|
{
|
|
y = cmVOS_Zero(y,n);
|
|
cmXfaderExecAudio(p->xf[i],n,NULL,p->iChCnt,x,y);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspNofM_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspNofM_t* p = (cmDspNofM_t*)inst;
|
|
unsigned i,j;
|
|
|
|
assert( evt->dstVarId < p->baseBaseOutNoId );
|
|
|
|
// store the incoming value
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
// if this is a fade time
|
|
if( evt->dstVarId == kXfadeMsNoId )
|
|
{
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
cmXfaderSetXfadeTime( p->xf[i], cmDspDouble(inst,kXfadeMsNoId) );
|
|
|
|
return rc;
|
|
}
|
|
|
|
// if this is an index selector
|
|
if( kSelIdxNoId == evt->dstVarId )
|
|
{
|
|
unsigned idx;
|
|
if((idx = cmDspUInt(inst,kSelIdxNoId)) >= p->iChCnt )
|
|
rc = cmDspInstErr(ctx,inst,kInvalidArgDspRC,"The selection index ('%i') is out of the channel range, (%i).",idx,p->iChCnt);
|
|
else
|
|
{
|
|
cmDspSetBool( ctx, inst, kBaseGateNoId+idx , true );
|
|
|
|
if( p->verboseFl )
|
|
cmRptPrintf(ctx->rpt,"nom seli:%i\n",idx);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// NOTE: the internal state DOES NOT CHANGE until a message arrives
|
|
// on the 'cmd' port
|
|
|
|
// if anything arrives on the command port - then read the gate states and rebuild the map
|
|
if( evt->dstVarId == kCmdNoId )
|
|
{
|
|
|
|
unsigned cmdSymId = cmDspSymbol(inst,kCmdNoId);
|
|
|
|
// if cmdSymId == 'on' then turn on all selection gates
|
|
if(cmdSymId == p->onSymId )
|
|
{
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
cmDspSetBool(ctx,inst,kBaseGateNoId+i,true);
|
|
}
|
|
else
|
|
// if cmdSymId == 'off' then turn off all selection gates
|
|
if( cmdSymId == p->offSymId )
|
|
{
|
|
if( p->verboseFl )
|
|
cmRptPrintf(ctx->rpt,"nom: off\n");
|
|
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
cmDspSetBool(ctx,inst,kBaseGateNoId+i,false);
|
|
}
|
|
else
|
|
// cmdSymId == 'print' then print the in/out map[]
|
|
if( cmdSymId == cmSymTblId(ctx->stH,"print") )
|
|
{
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
cmRptPrintf(ctx->rpt,"%i:%i ",i,p->map[i]);
|
|
cmRptPrintf(ctx->rpt,"\n");
|
|
cmRptPrintf(ctx->rpt,"%i usecs\n",ctx->execDurUsecs);
|
|
p->printFl = !p->printFl;
|
|
}
|
|
|
|
if( p->verboseFl )
|
|
cmRptPrintf(ctx->rpt,"Nom: %s ", inst->symId != cmInvalidId ? cmSymTblLabel(ctx->stH,inst->symId) : "");
|
|
|
|
// 5/26
|
|
if( p->iChCnt == p->oChCnt )
|
|
cmVOU_Fill(p->map,p->oChCnt,cmInvalidIdx);
|
|
|
|
// for each input
|
|
for(i=0,j=0; i<p->iChCnt; ++i)
|
|
{
|
|
|
|
// if this input is switched on
|
|
if( cmDspBool(inst,kBaseGateNoId+i) )
|
|
{
|
|
if( j >= p->oChCnt )
|
|
{
|
|
cmDspInstErr(ctx,inst,kVarNotValidDspRC,"To many inputs have been turned on for %i outputs.",p->oChCnt);
|
|
break;
|
|
}
|
|
|
|
// assign input i to output j
|
|
p->map[j] = i;
|
|
|
|
// fade in ch i and fade out all others
|
|
cmXfaderSelectOne(p->xf[j],i);
|
|
|
|
++j;
|
|
|
|
if( p->verboseFl )
|
|
cmRptPrintf(ctx->rpt,"%i ",i);
|
|
|
|
}
|
|
else // 5/26
|
|
{
|
|
if( p->iChCnt == p->oChCnt )
|
|
++j;
|
|
}
|
|
}
|
|
|
|
// deselect all other output channels
|
|
//for(; j<p->oChCnt; ++j)
|
|
//{
|
|
// p->map[j] = cmInvalidIdx;
|
|
// cmXfaderAllOff(p->xf[j]);
|
|
//}
|
|
// 5/26
|
|
if( p->iChCnt == p->oChCnt )
|
|
{
|
|
for(j=0; j<p->oChCnt; ++j)
|
|
if( p->map[j] == cmInvalidIdx )
|
|
cmXfaderAllOff(p->xf[j]);
|
|
}
|
|
else
|
|
{
|
|
for(; j<p->oChCnt; ++j)
|
|
{
|
|
p->map[j] = cmInvalidIdx;
|
|
cmXfaderAllOff(p->xf[j]);
|
|
}
|
|
}
|
|
|
|
|
|
if( p->verboseFl )
|
|
cmRptPrintf(ctx->rpt,"\n");
|
|
|
|
// zero the audio buffers of unused output channels
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
if( p->map[i] == cmInvalidIdx )
|
|
cmDspZeroAudioBuf(ctx,inst,p->baseOutAudioNoId+i);
|
|
|
|
}
|
|
|
|
|
|
// if this is an input data event
|
|
if( p->baseBaseInNoId <= evt->dstVarId && evt->dstVarId < p->baseBaseOutNoId )
|
|
{
|
|
// get the input channel this event occurred on
|
|
unsigned iChIdx = (evt->dstVarId - p->baseBaseInNoId) % p->iChCnt;
|
|
|
|
// is iChIdx mapped to an output ...
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
if( p->map[i] == iChIdx )
|
|
break;
|
|
|
|
// ... no - nothing else to do
|
|
if( i==p->oChCnt )
|
|
return kOkDspRC;
|
|
|
|
// ... yes set the output ...
|
|
|
|
// double
|
|
if( p->baseInFloatNoId <= evt->dstVarId && evt->dstVarId < p->baseInFloatNoId + p->iChCnt )
|
|
{
|
|
cmDspSetDouble(ctx,inst,p->baseOutFloatNoId + i, cmDspDouble(inst,evt->dstVarId));
|
|
}
|
|
else
|
|
|
|
// bool
|
|
if( p->baseInBoolNoId <= evt->dstVarId && evt->dstVarId < p->baseInBoolNoId + p->iChCnt )
|
|
{
|
|
cmDspSetBool(ctx,inst,p->baseOutBoolNoId + i, cmDspBool(inst,evt->dstVarId));
|
|
if(p->printFl)
|
|
cmRptPrintf(ctx->rpt,"%i %i\n",p->baseOutBoolNoId + i, cmDspBool(inst,evt->dstVarId));
|
|
}
|
|
else
|
|
|
|
// symbol
|
|
if( p->baseInSymNoId <= evt->dstVarId && evt->dstVarId < p->baseInSymNoId + p->iChCnt )
|
|
cmDspSetSymbol(ctx,inst,p->baseOutSymNoId + i, cmDspSymbol(inst,evt->dstVarId));
|
|
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmNofMClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmNofM_DC,ctx,"NofM",
|
|
NULL,
|
|
_cmDspNofM_Alloc,
|
|
_cmDspNofM_Free,
|
|
_cmDspNofM_Reset,
|
|
_cmDspNofM_Exec,
|
|
_cmDspNofM_Recv,
|
|
NULL,NULL,
|
|
"N of M Switch");
|
|
|
|
return &_cmNofM_DC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDsp1ofN file_desc:"Pass 1 or N possible inputs to the output." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kInChCnt1oId,
|
|
kInChIdx1oId,
|
|
kOutFloat1oId,
|
|
kOutBool1oId,
|
|
kOutSym1oId,
|
|
kOutAudio1oId,
|
|
kBaseInFloat1oId
|
|
};
|
|
|
|
cmDspClass_t _cm1ofN_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
|
|
unsigned iChCnt;
|
|
unsigned oChCnt;
|
|
|
|
unsigned iChIdx;
|
|
|
|
unsigned baseBaseIn1oId; // first data input port id
|
|
|
|
unsigned baseInFloat1oId;
|
|
unsigned baseInBool1oId;
|
|
unsigned baseInSym1oId;
|
|
unsigned baseInAudio1oId;
|
|
|
|
unsigned baseOutFloat1oId;
|
|
unsigned baseOutBool1oId;
|
|
unsigned baseOutSym1oId;
|
|
unsigned baseOutAudio1oId;
|
|
|
|
|
|
} cmDsp1ofN_t;
|
|
|
|
cmDspInst_t* _cmDsp1ofN_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The '1ofN' constructor must given input channel count.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int iChCnt = va_arg(vl,int);
|
|
|
|
unsigned baseInFloat1oId = kBaseInFloat1oId;
|
|
unsigned baseInBool1oId = baseInFloat1oId + iChCnt;
|
|
unsigned baseInSym1oId = baseInBool1oId + iChCnt;
|
|
unsigned baseInAudio1oId = baseInSym1oId + iChCnt;
|
|
|
|
cmDsp1ofN_t* p = cmDspInstAllocV(cmDsp1ofN_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "ichs", kInChCnt1oId, 0, 0, kUIntDsvFl | kReqArgDsvFl,"Input channel count.",
|
|
1, "chidx", kInChIdx1oId, 0, 0, kUIntDsvFl | kReqArgDsvFl | kInDsvFl, "Input channel selector index.",
|
|
1, "f-out", kOutFloat1oId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Float output",
|
|
1, "b-out", kOutBool1oId, 0, 0, kBoolDsvFl | kOutDsvFl, "Bool output",
|
|
1, "s-out", kOutSym1oId, 0, 0, kSymDsvFl | kOutDsvFl, "Symbol output",
|
|
1, "a-out", kOutAudio1oId, 0, 1, kAudioBufDsvFl | kOutDsvFl, "Audio output",
|
|
iChCnt, "f-in", baseInFloat1oId, 0, 0, kDoubleDsvFl | kInDsvFl, "Float input",
|
|
iChCnt, "b-in", baseInBool1oId, 0, 0, kBoolDsvFl | kInDsvFl, "Bool input",
|
|
iChCnt, "s-in", baseInSym1oId, 0, 0, kSymDsvFl | kInDsvFl, "Symbol input",
|
|
iChCnt, "a-in", baseInAudio1oId, 0, 0, kAudioBufDsvFl | kInDsvFl, "Audio input",
|
|
0 );
|
|
|
|
p->iChCnt = iChCnt;
|
|
|
|
p->baseBaseIn1oId = kBaseInFloat1oId;
|
|
|
|
p->baseInFloat1oId = baseInFloat1oId;
|
|
p->baseInBool1oId = baseInBool1oId;
|
|
p->baseInSym1oId = baseInSym1oId;
|
|
p->baseInAudio1oId = baseInAudio1oId;
|
|
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutFloat1oId, 0.0, 0.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, kOutBool1oId, false, false );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kOutSym1oId, cmInvalidId );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDsp1ofN_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDsp1ofN_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
cmDspZeroAudioBuf(ctx,inst,kOutAudio1oId);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDsp1ofN_Exec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDsp1ofN_t* p = (cmDsp1ofN_t*)inst;
|
|
unsigned iChIdx = cmDspUInt(inst,kInChIdx1oId);
|
|
cmSample_t* dp = cmDspAudioBuf(ctx,inst,kOutAudio1oId,0);
|
|
const cmSample_t* sp = cmDspAudioBuf(ctx,inst,p->baseInAudio1oId + iChIdx ,0);
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,kOutAudio1oId,0);
|
|
|
|
if( dp != NULL )
|
|
{
|
|
if( sp == NULL )
|
|
cmVOS_Zero(dp,n);
|
|
else
|
|
cmVOS_Copy(dp,n,sp);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDsp1ofN_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDsp1ofN_t* p = (cmDsp1ofN_t*)inst;
|
|
|
|
|
|
// ignore out of range input channel
|
|
if( evt->dstVarId == kInChIdx1oId && cmDsvGetUInt(evt->valuePtr) >= p->iChCnt )
|
|
{
|
|
cmDspInstErr(ctx,inst,kVarNotValidDspRC,"The selector channel index %i is out of range.",cmDsvGetUInt(evt->valuePtr));
|
|
return kOkDspRC;
|
|
}
|
|
|
|
// store the incoming value
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
|
|
// if this is an input data event
|
|
if( p->baseBaseIn1oId <= evt->dstVarId )
|
|
{
|
|
// get the input channel this event occurred on
|
|
unsigned iChIdx = (evt->dstVarId - p->baseBaseIn1oId) % p->iChCnt;
|
|
|
|
// if the event did not arrive on the selected input channel - there is nothing else to do
|
|
if( iChIdx != cmDspUInt(inst,kInChIdx1oId) )
|
|
return kOkDspRC;
|
|
|
|
// The event arrived on the input channel - send it out the output
|
|
|
|
// double
|
|
if( p->baseInFloat1oId <= evt->dstVarId && evt->dstVarId < p->baseInFloat1oId + p->iChCnt )
|
|
cmDspSetDouble(ctx,inst,kOutFloat1oId, cmDspDouble(inst,evt->dstVarId));
|
|
else
|
|
|
|
// bool
|
|
if( p->baseInBool1oId <= evt->dstVarId && evt->dstVarId < p->baseInBool1oId + p->iChCnt )
|
|
cmDspSetBool(ctx,inst,kOutBool1oId, cmDspBool(inst,evt->dstVarId));
|
|
else
|
|
|
|
// symbol
|
|
if( p->baseInSym1oId <= evt->dstVarId && evt->dstVarId < p->baseInSym1oId + p->iChCnt )
|
|
cmDspSetSymbol(ctx,inst,kOutSym1oId, cmDspSymbol(inst,evt->dstVarId));
|
|
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cm1ofNClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cm1ofN_DC,ctx,"1ofN",
|
|
NULL,
|
|
_cmDsp1ofN_Alloc,
|
|
_cmDsp1ofN_Free,
|
|
_cmDsp1ofN_Reset,
|
|
_cmDsp1ofN_Exec,
|
|
_cmDsp1ofN_Recv,
|
|
NULL,NULL,
|
|
" 1 of N Switch");
|
|
|
|
return &_cm1ofN_DC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDsp1Up file_desc:"Send 'true' on the selected channel and 'false' on the deselected channel." kw:[sunit] }
|
|
|
|
// Send a 'true' out on the selected channel.
|
|
// Send a 'false' out on the deselected channel.
|
|
enum
|
|
{
|
|
kChCnt1uId,
|
|
kSel1uId,
|
|
kBaseOut1uId
|
|
};
|
|
|
|
cmDspClass_t _cm1Up_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
|
|
} cmDsp1Up_t;
|
|
|
|
cmDspInst_t* _cmDsp1Up_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The '1Up' constructor must given output channel count.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int chCnt = va_arg(vl,int);
|
|
|
|
cmDsp1Up_t* p = cmDspInstAllocV(cmDsp1Up_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "chcnt", kChCnt1uId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Output channel count.",
|
|
1, "sel", kSel1uId, 0, 0, kUIntDsvFl | kOptArgDsvFl | kInDsvFl, "Channel index selector.",
|
|
chCnt, "out", kBaseOut1uId, 0, 0, kBoolDsvFl | kOutDsvFl, "Gate outputs",
|
|
0 );
|
|
|
|
|
|
unsigned i;
|
|
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kSel1uId, 0.0, 0.0 );
|
|
|
|
for(i=0; i<chCnt; ++i)
|
|
cmDspSetDefaultBool( ctx, &p->inst, kBaseOut1uId + i, false, false );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDsp1Up_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
unsigned chIdx = cmDspUInt(inst,kSel1uId);
|
|
unsigned chCnt = cmDspUInt(inst,kChCnt1uId);
|
|
unsigned i;
|
|
for(i=0; i<chCnt; ++i)
|
|
cmDspSetBool(ctx,inst,kBaseOut1uId+i, i == chIdx );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDsp1Up_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if( evt->dstVarId == kSel1uId)
|
|
{
|
|
unsigned chIdx = cmDspUInt(inst,kSel1uId);
|
|
unsigned chCnt = cmDspUInt(inst,kChCnt1uId);
|
|
|
|
// turn off the previously selected channel
|
|
if( chIdx != cmInvalidIdx && chIdx < chCnt )
|
|
cmDspSetBool(ctx,inst,kBaseOut1uId+chIdx,false);
|
|
|
|
// set the new channel index
|
|
cmDspSetEvent(ctx,inst,evt);
|
|
|
|
// get the new channel index
|
|
chIdx = cmDspUInt(inst,kSel1uId);
|
|
|
|
// send the new channel index
|
|
if( chIdx != cmInvalidIdx && chIdx < chCnt )
|
|
cmDspSetBool(ctx,inst,kBaseOut1uId+chIdx,true);
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cm1UpClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cm1Up_DC,ctx,"1Up",
|
|
NULL,
|
|
_cmDsp1Up_Alloc,
|
|
NULL,
|
|
_cmDsp1Up_Reset,
|
|
NULL,
|
|
_cmDsp1Up_Recv,
|
|
NULL,NULL,
|
|
"Set one input high and all others low.");
|
|
|
|
return &_cm1Up_DC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspGateToSym file_desc:"Convert a 'true'/'false' gate to an 'on'/'off' symbol." kw:[sunit] }
|
|
//
|
|
enum
|
|
{
|
|
kOnSymGsId,
|
|
kOffSymGsId,
|
|
kOnGsId,
|
|
kOffGsId,
|
|
kBothGsId,
|
|
kOutGsId
|
|
};
|
|
|
|
cmDspClass_t _cmGateToSym_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
} cmDspGateToSym_t;
|
|
|
|
cmDspInst_t* _cmDspGateToSym_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspGateToSym_t* p = cmDspInstAllocV(cmDspGateToSym_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "on_sym", kOnSymGsId, 0, 0, kSymDsvFl | kInDsvFl | kOptArgDsvFl,"'on' symbol id (default:'on')",
|
|
1, "off_sym",kOffSymGsId, 0, 0, kSymDsvFl | kInDsvFl | kOptArgDsvFl,"'off' symbol id (default:'off')",
|
|
1, "on", kOnGsId, 0, 0, kBoolDsvFl | kInDsvFl, "On - send out 'on' symbol when a 'true' is received.",
|
|
1, "off", kOffGsId, 0, 0, kBoolDsvFl | kInDsvFl, "Off - send out 'off' symbol when a 'false' is received.",
|
|
1, "both", kBothGsId, 0, 0, kBoolDsvFl | kInDsvFl, "Send 'on' on 'true' and 'off' on 'false'.",
|
|
1, "out", kOutGsId, 0, 0, kSymDsvFl | kOutDsvFl, "Output",
|
|
0 );
|
|
|
|
|
|
unsigned onSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"on");
|
|
unsigned offSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"off");
|
|
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kOnSymGsId, onSymId );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kOffSymGsId, offSymId );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kOutGsId, cmInvalidId );
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspGateToSym_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspGateToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
unsigned onSymId = cmDspSymbol(inst,kOnSymGsId);
|
|
unsigned offSymId = cmDspSymbol(inst,kOffSymGsId);
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kOnGsId:
|
|
if( cmDspBool(inst,kOnGsId) )
|
|
cmDspSetSymbol(ctx,inst,kOutGsId,onSymId);
|
|
break;
|
|
|
|
case kOffGsId:
|
|
if( !cmDspBool(inst,kOffGsId) )
|
|
cmDspSetSymbol(ctx,inst,kOutGsId,offSymId);
|
|
break;
|
|
|
|
case kBothGsId:
|
|
cmDspSetSymbol(ctx,inst, kOutGsId, cmDspBool(inst,kBothGsId) ? onSymId : offSymId);
|
|
break;
|
|
}
|
|
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmGateToSymClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmGateToSym_DC,ctx,"GateToSym",
|
|
NULL,
|
|
_cmDspGateToSym_Alloc,
|
|
NULL,
|
|
_cmDspGateToSym_Reset,
|
|
NULL,
|
|
_cmDspGateToSym_Recv,
|
|
NULL,NULL,
|
|
"Convert a 'true'/'false' gate to an 'on'/'off' symbol.");
|
|
|
|
return &_cmGateToSym_DC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspPortToSym file_desc:"Send a pre-defined symbol every time a message arrives a given input port." kw:[sunit] }
|
|
enum
|
|
{
|
|
kOutPtsId,
|
|
kBaseInPtsId
|
|
};
|
|
|
|
cmDspClass_t _cmPortToSym_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned* symIdArray;
|
|
unsigned symIdCnt;
|
|
unsigned baseOutPtsId;
|
|
} cmDspPortToSym_t;
|
|
|
|
cmDspInst_t* _cmDspPortToSym_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
if( va_cnt < 1 )
|
|
{
|
|
va_end(vl1);
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'PortToSym' constructor argument list must contain at least one symbol label.");
|
|
return NULL;
|
|
}
|
|
|
|
unsigned symCnt = va_cnt;
|
|
unsigned argCnt = 1 + 2*symCnt;
|
|
cmDspVarArg_t args[argCnt+1];
|
|
|
|
unsigned* symIdArray = cmMemAllocZ(unsigned,symCnt);
|
|
unsigned baseOutPtsId = kBaseInPtsId + symCnt;
|
|
|
|
// setup the output port arg recd
|
|
cmDspArgSetup(ctx,args,"out",cmInvalidId,kOutPtsId,0,0,kOutDsvFl | kSymDsvFl, "Output" );
|
|
|
|
unsigned i;
|
|
|
|
for(i=0; i<symCnt; ++i)
|
|
{
|
|
// get the symbol label
|
|
const cmChar_t* symLabel = va_arg(vl,const char*);
|
|
assert( symLabel != NULL );
|
|
|
|
// register the symbol
|
|
symIdArray[i] = cmSymTblRegisterSymbol(ctx->stH,symLabel);
|
|
|
|
cmDspArgSetup(ctx, args+kBaseInPtsId+i, symLabel, cmInvalidId, kBaseInPtsId+i, 0, 0, kInDsvFl | kTypeDsvMask, cmTsPrintfH(ctx->lhH,"%s Input.",symLabel) );
|
|
|
|
cmDspArgSetup(ctx, args+baseOutPtsId+i, symLabel, cmInvalidId, baseOutPtsId+i, 0, 0, kOutDsvFl | kSymDsvFl, cmTsPrintfH(ctx->lhH,"%s Output.",symLabel) );
|
|
|
|
}
|
|
|
|
cmDspArgSetupNull(args + argCnt);
|
|
|
|
cmDspPortToSym_t* p = cmDspInstAlloc(cmDspPortToSym_t,ctx,classPtr,args,instSymId,id,storeSymId,0,vl1);
|
|
|
|
p->symIdCnt = symCnt;
|
|
p->symIdArray = symIdArray;
|
|
p->baseOutPtsId = baseOutPtsId;
|
|
|
|
cmDspSetDefaultSymbol(ctx,&p->inst,kOutPtsId,cmInvalidId);
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
cmDspRC_t _cmDspPortToSym_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspPortToSym_t* p = (cmDspPortToSym_t*)inst;
|
|
cmMemFree(p->symIdArray);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPortToSym_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return cmDspApplyAllDefaults(ctx,inst);
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspPortToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspPortToSym_t* p = (cmDspPortToSym_t*)inst;
|
|
|
|
// if a msg of any type is recieved on an input port - send out the associated symbol
|
|
if( kBaseInPtsId <= evt->dstVarId && evt->dstVarId < kBaseInPtsId + p->symIdCnt )
|
|
{
|
|
unsigned idx = evt->dstVarId - kBaseInPtsId;
|
|
assert( idx < p->symIdCnt );
|
|
cmDspSetSymbol(ctx,inst,p->baseOutPtsId + idx, p->symIdArray[idx]);
|
|
return cmDspSetSymbol(ctx,inst,kOutPtsId,p->symIdArray[ evt->dstVarId - kBaseInPtsId ]);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmPortToSymClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmPortToSym_DC,ctx,"PortToSym",
|
|
NULL,
|
|
_cmDspPortToSym_Alloc,
|
|
_cmDspPortToSym_Free,
|
|
_cmDspPortToSym_Reset,
|
|
NULL,
|
|
_cmDspPortToSym_Recv,
|
|
NULL,NULL,
|
|
"If a message of any kind is received on a port then send the symbol associated with the port.");
|
|
|
|
return &_cmPortToSym_DC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspRouter file_desc:"Route the input value to one of multiple output ports." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kOutChCntRtId,
|
|
kOutChIdxRtId,
|
|
kInFloatRtId,
|
|
kInBoolRtId,
|
|
kInSymRtId,
|
|
kInAudioRtId,
|
|
kBaseOutFloatRtId
|
|
};
|
|
|
|
cmDspClass_t _cmRouter_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
|
|
unsigned oChCnt;
|
|
unsigned oChIdx;
|
|
|
|
unsigned baseBaseOutRtId; // first data input port id
|
|
|
|
unsigned baseInFloatRtId;
|
|
unsigned baseInBoolRtId;
|
|
unsigned baseInSymRtId;
|
|
unsigned baseInAudioRtId;
|
|
|
|
unsigned baseOutFloatRtId;
|
|
unsigned baseOutBoolRtId;
|
|
unsigned baseOutSymRtId;
|
|
unsigned baseOutAudioRtId;
|
|
|
|
|
|
} cmDspRouter_t;
|
|
|
|
cmDspInst_t* _cmDspRouter_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'Router' constructor must be given an output channel count.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int oChCnt = va_arg(vl,int);
|
|
|
|
unsigned baseOutFloatRtId = kBaseOutFloatRtId;
|
|
unsigned baseOutBoolRtId = baseOutFloatRtId + oChCnt;
|
|
unsigned baseOutSymRtId = baseOutBoolRtId + oChCnt;
|
|
unsigned baseOutAudioRtId = baseOutSymRtId + oChCnt;
|
|
|
|
cmDspRouter_t* p = cmDspInstAllocV(cmDspRouter_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "ochs", kOutChCntRtId, 0, 0, kUIntDsvFl | kReqArgDsvFl,"Output channel count.",
|
|
1, "sel", kOutChIdxRtId, 0, 0, kUIntDsvFl | kReqArgDsvFl | kInDsvFl, "Output channel index selector.",
|
|
1, "f-in", kInFloatRtId, 0, 0, kDoubleDsvFl | kInDsvFl, "Float input",
|
|
1, "b-in", kInBoolRtId, 0, 0, kBoolDsvFl | kInDsvFl, "Bool input",
|
|
1, "s-in", kInSymRtId, 0, 0, kSymDsvFl | kInDsvFl, "Symbol input",
|
|
1, "a-in", kInAudioRtId, 0, 0, kAudioBufDsvFl | kInDsvFl, "Audio input",
|
|
oChCnt, "f-out", baseOutFloatRtId, 0, 0, kDoubleDsvFl | kOutDsvFl, "Float output",
|
|
oChCnt, "b-out", baseOutBoolRtId, 0, 0, kBoolDsvFl | kOutDsvFl, "Bool output",
|
|
oChCnt, "s-out", baseOutSymRtId, 0, 0, kSymDsvFl | kOutDsvFl, "Symbol output",
|
|
oChCnt, "a-out", baseOutAudioRtId, 0, 1, kAudioBufDsvFl | kOutDsvFl, "Audio output",
|
|
0 );
|
|
|
|
p->oChCnt = oChCnt;
|
|
|
|
p->baseBaseOutRtId = kBaseOutFloatRtId;
|
|
p->baseOutFloatRtId = baseOutFloatRtId;
|
|
p->baseOutBoolRtId = baseOutBoolRtId;
|
|
p->baseOutSymRtId = baseOutSymRtId;
|
|
p->baseOutAudioRtId = baseOutAudioRtId;
|
|
|
|
unsigned i;
|
|
for(i=0; i<oChCnt; ++i)
|
|
{
|
|
cmDspSetDefaultDouble( ctx, &p->inst, baseOutFloatRtId+i, 0.0, 0.0 );
|
|
cmDspSetDefaultBool( ctx, &p->inst, baseOutBoolRtId+i, false, false );
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, baseOutSymRtId+i, cmInvalidId );
|
|
}
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRouter_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRouter_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspRouter_t* p = (cmDspRouter_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<p->oChCnt; ++i)
|
|
cmDspZeroAudioBuf(ctx,inst,p->baseOutAudioRtId+i);
|
|
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRouter_Exec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspRouter_t* p = (cmDspRouter_t*)inst;
|
|
unsigned oChIdx = cmDspUInt(inst,kOutChIdxRtId);
|
|
cmSample_t* dp = cmDspAudioBuf(ctx,inst,p->baseOutAudioRtId + oChIdx,0);
|
|
const cmSample_t* sp = cmDspAudioBuf(ctx,inst,kInAudioRtId ,0);
|
|
unsigned n = cmDspAudioBufSmpCount(ctx,inst,p->baseOutAudioRtId,0);
|
|
|
|
if( dp != NULL )
|
|
{
|
|
if( sp == NULL )
|
|
cmVOS_Zero(dp,n);
|
|
else
|
|
cmVOS_Copy(dp,n,sp);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspRouter_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspRouter_t* p = (cmDspRouter_t*)inst;
|
|
|
|
|
|
// ignore out of range output channel selection
|
|
if( evt->dstVarId == kOutChIdxRtId && cmDsvGetUInt(evt->valuePtr) >= p->oChCnt )
|
|
{
|
|
cmDspInstErr(ctx,inst,kVarNotValidDspRC,"The selector channel index %i is out of of range.",cmDsvGetUInt(evt->valuePtr));
|
|
return kOkDspRC;
|
|
}
|
|
|
|
// if( evt->dstVarId == kOutChIdxRtId && cmDsvGetUInt(evt->valuePtr) < p->oChCnt )
|
|
// {
|
|
// const cmChar_t* symLbl = cmSymTblLabel(ctx->stH,inst->symId);
|
|
// cmDspInstErr(ctx,inst,kOkDspRC,"Router: ch:%i %s\n",cmDsvGetUInt(evt->valuePtr),symLbl==NULL?"":symLbl);
|
|
// }
|
|
|
|
// store the incoming value
|
|
if( evt->dstVarId < p->baseBaseOutRtId )
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
unsigned chIdx = cmDspUInt(inst,kOutChIdxRtId);
|
|
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kInFloatRtId:
|
|
cmDspSetDouble(ctx,inst,p->baseOutFloatRtId + chIdx, cmDspDouble(inst,kInFloatRtId) );
|
|
break;
|
|
|
|
case kInBoolRtId:
|
|
cmDspSetBool(ctx,inst,p->baseOutBoolRtId + chIdx, cmDspBool(inst,kInBoolRtId) );
|
|
break;
|
|
|
|
case kInSymRtId:
|
|
cmDspSetSymbol(ctx,inst,p->baseOutSymRtId + chIdx, cmDspSymbol(inst,kInSymRtId));
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmRouterClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmRouter_DC,ctx,"Router",
|
|
NULL,
|
|
_cmDspRouter_Alloc,
|
|
_cmDspRouter_Free,
|
|
_cmDspRouter_Reset,
|
|
_cmDspRouter_Exec,
|
|
_cmDspRouter_Recv,
|
|
NULL,NULL,
|
|
"1 to N Router");
|
|
|
|
return &_cmRouter_DC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspAvailCh file_desc:"Track active an inactive processing channels." kw:[sunit] }
|
|
//
|
|
// Purpose: AvailCh can be used to implement a channel switching circuit.
|
|
//
|
|
// Inputs:
|
|
// chs - The count of channels. Constructor only argument.
|
|
// trig - Any input causes the next available channel, i, to be enabled.
|
|
// gate[i] transmits 'true'. In 'exclusive (0) mode all active
|
|
// channels are then requested to shutdown by transmitting 'false' on
|
|
// gate[] - only the new channel will be active. In 'multi' (1) mode
|
|
// no signal is sent out the gate[].
|
|
// dis[chCnt] - Recieves a gate signal from an external object which indicates
|
|
// when a channel is no longer active. When a 'false' is received on dis[i]
|
|
// the channel i is marked as available. In 'multi' mode 'false' is
|
|
// then transmitted on gate[i].
|
|
// Outputs:
|
|
// gate[chCnt] - 'true' is transmitted when a channel is made active (see trig)
|
|
// 'false' is transmitted to notify the channel that it should shutdown.
|
|
// The channel is not considered actually shutdown until dis[i]
|
|
// recieves a 'false'.
|
|
// ch The next prospective next available channel is sent whenever it
|
|
// becomes available. A next channel becomes available when
|
|
// a channel is marked as inactive via dis[i] or when
|
|
// a new channel is made active, via trigger, and another
|
|
// channel active channel exists.
|
|
// Notes:
|
|
// The gate[] output is designed to work with the gate[] input of Xfader. When
|
|
// availCh.gate[] goes high Xfader fades in, when availCh.gate[] goes low
|
|
// Xfader fades out. The dis[] channel is designed to connect from Xfader.state[].
|
|
// Xfader.state[] goes low when a fade-out is complete, the connected AvailCh
|
|
// is then marked as available.
|
|
enum
|
|
{
|
|
kChCntAvId,
|
|
kModeAvId,
|
|
kTrigAvId,
|
|
kResetAvId,
|
|
kChIdxAvId,
|
|
kBaseDisInAvId,
|
|
|
|
kExclusiveModeAvId=0,
|
|
kMultiModeAvId=1
|
|
};
|
|
|
|
cmDspClass_t _cmAvailCh_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned chCnt;
|
|
unsigned baseDisInAvId;
|
|
unsigned baseGateOutAvId;
|
|
bool* stateArray;
|
|
unsigned nextAvailChIdx;
|
|
unsigned audioCycleCnt;
|
|
} cmDspAvailCh_t;
|
|
|
|
cmDspInst_t* _cmDspAvailCh_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
if( va_cnt < 1 )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'AvailCh' constructor must be given an channel count.");
|
|
return NULL;
|
|
}
|
|
|
|
va_list vl1;
|
|
va_copy(vl1,vl);
|
|
|
|
int chCnt = va_arg(vl,int);
|
|
|
|
if( chCnt <= 0 )
|
|
{
|
|
va_end(vl1);
|
|
cmDspClassErr(ctx,classPtr,kInvalidArgDspRC,"The 'AvailCh' constructor must be given a positive channel count.");
|
|
return NULL;
|
|
}
|
|
|
|
unsigned baseDisInAvId = kBaseDisInAvId;
|
|
unsigned baseGateOutAvId = baseDisInAvId + chCnt;
|
|
|
|
cmDspAvailCh_t* p = cmDspInstAllocV(cmDspAvailCh_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl1,
|
|
1, "chs", kChCntAvId, 0, 0, kUIntDsvFl | kReqArgDsvFl, "Channel count.",
|
|
1, "mode", kModeAvId, 0, 0, kUIntDsvFl | kInDsvFl, "Mode: 0=exclusive (dflt) 1=multi",
|
|
1, "trig", kTrigAvId, 0, 0, kTypeDsvMask | kInDsvFl, "Trigger the unit to select the next available channel.",
|
|
1, "reset", kResetAvId, 0, 0, kBoolDsvFl | kInDsvFl | kOutDsvFl, "Reset to default state",
|
|
1, "ch", kChIdxAvId, 0, 0, kUIntDsvFl | kOutDsvFl, "Currently selected channel.",
|
|
chCnt, "dis", baseDisInAvId, 0, 0, kBoolDsvFl | kInDsvFl, "Disable channel gate",
|
|
chCnt, "gate", baseGateOutAvId, 0, 0, kBoolDsvFl | kOutDsvFl, "Active channel gate",
|
|
0 );
|
|
|
|
p->chCnt = chCnt;
|
|
|
|
p->baseDisInAvId = baseDisInAvId;
|
|
p->baseGateOutAvId = baseGateOutAvId;
|
|
p->nextAvailChIdx = cmInvalidIdx;
|
|
p->audioCycleCnt = 0;
|
|
|
|
unsigned i;
|
|
for(i=0; i<chCnt; ++i)
|
|
{
|
|
cmDspSetDefaultBool( ctx, &p->inst, baseDisInAvId+i, false, false );
|
|
cmDspSetDefaultBool( ctx, &p->inst, baseGateOutAvId+i, false, false );
|
|
}
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kModeAvId, 0, kExclusiveModeAvId );
|
|
cmDspSetDefaultUInt( ctx, &p->inst, kChIdxAvId, 0, 0 );
|
|
|
|
va_end(vl1);
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAvailCh_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAvailCh_DoReset( cmDspCtx_t* ctx, cmDspInst_t* inst )
|
|
{
|
|
unsigned i;
|
|
cmDspAvailCh_t* p = (cmDspAvailCh_t*)inst;
|
|
|
|
|
|
// ch 0 is the channel receiving parameters
|
|
cmDspSetUInt(ctx,inst,kChIdxAvId,0);
|
|
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
cmDspSetBool(ctx, inst, p->baseDisInAvId + i, i==0); // disable all channels except ch zero
|
|
cmDspSetBool(ctx, inst, p->baseGateOutAvId + i, i==0); // enable channel 0
|
|
}
|
|
|
|
p->audioCycleCnt = 0;
|
|
p->nextAvailChIdx = cmInvalidIdx;
|
|
|
|
// transmit reset
|
|
cmDspSetBool(ctx,inst, kResetAvId, false );
|
|
|
|
return kOkDspRC;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspAvailCh_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
rc = _cmDspAvailCh_DoReset(ctx,inst);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void _cmDspAvailCh_SetNextAvailCh( cmDspCtx_t* ctx, cmDspInst_t* inst, bool warnFl, const char* label )
|
|
{
|
|
cmDspAvailCh_t* p = (cmDspAvailCh_t*)inst;
|
|
unsigned i;
|
|
|
|
// if a valid next avail ch already exists then do nothing
|
|
if( p->nextAvailChIdx != cmInvalidIdx )
|
|
return;
|
|
|
|
// for each channel
|
|
for(i=0; i<p->chCnt; ++i)
|
|
{
|
|
// the channel's active state is held in the 'dis' variable.
|
|
bool activeFl = cmDspBool(inst,p->baseDisInAvId+i);
|
|
|
|
// if ch[i] is the first avail inactive channel
|
|
if( !activeFl )
|
|
{
|
|
p->nextAvailChIdx = i; // then make it the next available channel
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// if no available channels were found
|
|
if( p->nextAvailChIdx == cmInvalidIdx )
|
|
{
|
|
if( warnFl )
|
|
cmDspInstErr(ctx,inst,kInvalidStateDspRC,"No available channels exist.");
|
|
}
|
|
else
|
|
{
|
|
// Notify the external world which channel is to be used next.
|
|
// This allows routers which are switching parameters between
|
|
// xfade channels to switch new parameter values to go to the
|
|
// next available channel rather than the current channel.
|
|
// The next available channel will then be faded up with the
|
|
// new parameters on the next trigger command.
|
|
cmDspSetUInt(ctx,inst,kChIdxAvId,p->nextAvailChIdx);
|
|
}
|
|
}
|
|
|
|
cmDspRC_t _cmDspAvailCh_Exec( cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAvailCh_t* p = (cmDspAvailCh_t*)inst;
|
|
|
|
p->audioCycleCnt += 1;
|
|
|
|
// Setting the next available channel here solves the problem of sending the
|
|
// first 'ch' output after the program starts executing.
|
|
// The problem is that 'ch' should be set to 0 for the first
|
|
// execution cycle so that parameters may be set to the initial active channel
|
|
// during the first cycle. After the first cycle however parameters should be
|
|
// sent to the next channel which will be faded up. Setting
|
|
// 'ch' here accomplishes this without relying on an external signal.
|
|
// Note that we wait until the second cycle because we don't know where
|
|
// this 'availCh' will be in the execution cycle relative to other processors.
|
|
// If it is at the beginning then other processors that might be setting
|
|
// initial parameters will not have had a chance to run before the
|
|
// 'ch' change. Waiting unitl the second cycle guarantees that all the
|
|
// other processors had at least one chance to run.
|
|
if( p->audioCycleCnt == 2 )
|
|
{
|
|
_cmDspAvailCh_SetNextAvailCh(ctx,inst,true,"exec");
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspAvailCh_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspAvailCh_t* p = (cmDspAvailCh_t*)inst;
|
|
|
|
bool exclModeFl = cmDspUInt(inst, kModeAvId ) == kExclusiveModeAvId;
|
|
|
|
// if this is a reset
|
|
if( evt->dstVarId == kResetAvId )
|
|
{
|
|
return _cmDspAvailCh_DoReset(ctx,inst);
|
|
}
|
|
|
|
// if this is a trigger
|
|
if( evt->dstVarId == kTrigAvId )
|
|
{
|
|
// if no available channels were previously found
|
|
if( p->nextAvailChIdx == cmInvalidIdx )
|
|
cmDspInstErr(ctx,inst,kInvalidStateDspRC,"There are no available channels to trigger.");
|
|
else
|
|
{
|
|
// indicate that ch[nexAvailChIdx] is no longer available
|
|
cmDspSetBool(ctx, inst, p->baseDisInAvId + p->nextAvailChIdx, true);
|
|
|
|
// raise the gate to start the xfade.
|
|
cmDspSetBool(ctx, inst, p->baseGateOutAvId + p->nextAvailChIdx, true);
|
|
|
|
if( exclModeFl )
|
|
{
|
|
unsigned i;
|
|
for(i=0; i<p->chCnt; ++i)
|
|
if( i!=p->nextAvailChIdx && cmDspBool(inst,p->baseDisInAvId+i) )
|
|
cmDspSetBool(ctx,inst, p->baseGateOutAvId+i, false );
|
|
}
|
|
|
|
// invalidate nextAvailChIdx
|
|
p->nextAvailChIdx = cmInvalidIdx;
|
|
|
|
// It may be possible to know the next avail ch so try to set it here.
|
|
_cmDspAvailCh_SetNextAvailCh(ctx, inst, false, "trig" );
|
|
|
|
}
|
|
|
|
|
|
return rc;
|
|
}
|
|
|
|
// if this is an incoming disable message.
|
|
if( p->baseDisInAvId <= evt->dstVarId && evt->dstVarId < p->baseDisInAvId+p->chCnt && cmDsvGetBool(evt->valuePtr) == false)
|
|
{
|
|
cmDspSetEvent(ctx,inst,evt);
|
|
|
|
// a channel was disabled so a new channel should be available for selection
|
|
if( p->audioCycleCnt > 0 )
|
|
_cmDspAvailCh_SetNextAvailCh(ctx, inst, true, "dis" );
|
|
|
|
if( !exclModeFl )
|
|
cmDspSetBool(ctx, inst, p->baseGateOutAvId + (evt->dstVarId - p->baseDisInAvId), false);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmAvailChClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmAvailCh_DC,ctx,"AvailCh",
|
|
NULL,
|
|
_cmDspAvailCh_Alloc,
|
|
_cmDspAvailCh_Free,
|
|
_cmDspAvailCh_Reset,
|
|
_cmDspAvailCh_Exec,
|
|
_cmDspAvailCh_Recv,
|
|
NULL,NULL,
|
|
"Enable the next availabled channel");
|
|
|
|
return &_cmAvailCh_DC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspPreset file_desc:"Store and recall preset. Show a preset list user interface unit." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kGroupSymPrId,
|
|
kLabelPrId,
|
|
kCmdPrId,
|
|
kDonePrId,
|
|
kListPrId,
|
|
kSelPrId
|
|
};
|
|
|
|
cmDspClass_t _cmPreset_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned storeCmdSymId;
|
|
unsigned recallCmdSymId;
|
|
unsigned doneSymId;
|
|
cmJsonH_t jsH;
|
|
cmJsonNode_t* np;
|
|
} cmDspPreset_t;
|
|
|
|
cmDspRC_t _cmDspPresetUpdateList( cmDspCtx_t* ctx, cmDspPreset_t* p, unsigned groupSymId )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
// initialize the JSON tree
|
|
if( cmJsonInitialize(&p->jsH,ctx->cmCtx) != kOkJsRC )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"JSON preset list handle initialization failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// create the JSON tree root container
|
|
if( cmJsonCreateObject(p->jsH,NULL) == NULL )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"JSON preset list root object create failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// if a valid preset group symbol was given
|
|
if( groupSymId != cmInvalidId )
|
|
{
|
|
// get the JSON list containing the preset labels and symId's for this preset group
|
|
if( cmDspSysPresetPresetJsonList(ctx->dspH, groupSymId, &p->jsH ) != kOkDspRC )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kSubSysFailDspRC,"Request for a preset list failed.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// get a pointer to the JSON 'presetArray' array node
|
|
if(( p->np = cmJsonFindValue(p->jsH,"presetArray",NULL,kArrayTId)) == NULL )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"Preset list is empty or synatax is not recognized.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// set the JSON list
|
|
if((rc = cmDspSetJson(ctx,&p->inst,kListPrId,p->np)) != kOkDspRC )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,rc,"Preset list set failed.");
|
|
goto errLabel;
|
|
}
|
|
}
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPresetDoRecall( cmDspCtx_t* ctx, cmDspPreset_t* p, const cmChar_t* groupLabel, const cmChar_t* presetLabel )
|
|
{
|
|
cmDspRC_t rc;
|
|
|
|
// recall the preset
|
|
if(( rc = cmDspSysPresetRecall(ctx->dspH, groupLabel, presetLabel )) != kOkDspRC )
|
|
return cmDspInstErr(ctx,&p->inst,kSubSysFailDspRC,"Preset recall failed for group:'%s' preset:'%s'.",cmStringNullGuard(groupLabel),cmStringNullGuard(presetLabel));
|
|
|
|
// send out a notification that a new preset has been loaded
|
|
return cmDspSetSymbol(ctx,&p->inst,kDonePrId,p->doneSymId);
|
|
}
|
|
|
|
// selIdx is base 1, not base 0, because it references the JSON tree rows where the
|
|
// first row contains the titles.
|
|
cmDspRC_t _cmDspPresetListSelectRecall( cmDspCtx_t* ctx, cmDspPreset_t* p, unsigned selIdx )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
const cmChar_t* presetLabel;
|
|
const cmChar_t* groupLabel;
|
|
unsigned groupSymId;
|
|
unsigned presetSymId;
|
|
const cmJsonNode_t* rnp;
|
|
|
|
// validate the JSON tree
|
|
if( cmJsonIsValid(p->jsH) == false || p->np == NULL )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"Preset recall failed. The preset JSON tree does not exist.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// validate the group id
|
|
if( (groupSymId = cmDspSymbol(&p->inst,kGroupSymPrId)) == cmInvalidId )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"Preset recall failed. The preset group symbol has not been set.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// validate the selection index
|
|
if( selIdx >= cmJsonChildCount(p->np) )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kVarNotValidDspRC,"Preset recall failed. The preset index: %i is out of range 0-%i", selIdx, cmJsonChildCount(p->np));
|
|
goto errLabel;
|
|
}
|
|
|
|
// get the preset element
|
|
if(( rnp = cmJsonArrayElementC(p->np, selIdx )) == NULL )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"Preset recall failed. Unable to retrieve preset JSON element.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// verify the JSON syntax
|
|
assert( rnp->typeId==kArrayTId && cmJsonChildCount(rnp)==2 && cmJsonArrayElementC(rnp,1)->typeId == kIntTId );
|
|
|
|
// get the preset symbol id
|
|
if( cmJsonUIntValue( cmJsonArrayElementC(rnp,1), &presetSymId ) != kOkJsRC )
|
|
{
|
|
rc = cmDspInstErr(ctx,&p->inst,kJsonFailDspRC,"Preset recall failed. Unable to retrieve preset symbol id.");
|
|
goto errLabel;
|
|
}
|
|
|
|
// convert symbols to strings
|
|
groupLabel = cmSymTblLabel(ctx->stH,groupSymId);
|
|
presetLabel = cmSymTblLabel(ctx->stH,presetSymId);
|
|
|
|
rc = _cmDspPresetDoRecall(ctx,p,groupLabel,presetLabel);
|
|
|
|
errLabel:
|
|
return rc;
|
|
}
|
|
|
|
cmDspInst_t* _cmDspPreset_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
|
|
cmDspPreset_t* p = cmDspInstAllocV(cmDspPreset_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "sym", kGroupSymPrId, 0, 0, kInDsvFl | kSymDsvFl | kReqArgDsvFl, "Preset group symbol.",
|
|
1, "label", kLabelPrId, 0, 0, kInDsvFl | kStrzDsvFl | kOptArgDsvFl, "Preset label",
|
|
1, "cmd", kCmdPrId, 0, 0, kInDsvFl | kSymDsvFl, "Command input",
|
|
1, "done", kDonePrId, 0, 0, kOutDsvFl | kSymDsvFl, "Send 'done' symbol after preset recall.",
|
|
1, "list", kListPrId, 0, 0, kInDsvFl | kJsonDsvFl, "Preset list as a JSON array.",
|
|
1, "sel", kSelPrId, 0, 0, kInDsvFl | kUIntDsvFl, "Preset index selection index",
|
|
0 );
|
|
|
|
p->jsH = cmJsonNullHandle;
|
|
p->np = NULL;
|
|
p->storeCmdSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"store");
|
|
p->recallCmdSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"recall");
|
|
p->doneSymId = cmSymTblRegisterStaticSymbol(ctx->stH,"done");
|
|
|
|
cmDspSetDefaultBool( ctx, &p->inst, kDonePrId,false,false);
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kGroupSymPrId, cmInvalidId);
|
|
cmDspSetDefaultStrcz( ctx, &p->inst, kLabelPrId, NULL,"");
|
|
|
|
unsigned height = 5;
|
|
cmDspUiMsgListCreate(ctx, &p->inst, height, kListPrId, kSelPrId );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPreset_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspPreset_t* p = (cmDspPreset_t*)inst;
|
|
|
|
cmJsonFinalize(&p->jsH);
|
|
return kOkDspRC;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPreset_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc;
|
|
cmDspPreset_t* p = (cmDspPreset_t*)inst;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
_cmDspPresetUpdateList(ctx, p, cmDspSymbol(inst,kGroupSymPrId) );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspRC_t _cmDspPreset_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspPreset_t* p = (cmDspPreset_t*)inst;
|
|
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kListPrId:
|
|
return rc; // we don't yet handle lists arriving by the input port
|
|
|
|
case kSelPrId:
|
|
{
|
|
// sel idx is base 1 because the first row in the msg list contains the titles
|
|
unsigned selIdx = cmDsvGetUInt(evt->valuePtr);
|
|
if((rc = _cmDspPresetListSelectRecall(ctx,p,selIdx)) != kOkDspRC)
|
|
return rc;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) != kOkDspRC )
|
|
return rc;
|
|
|
|
// if this is a store or recall command
|
|
if( evt->dstVarId == kCmdPrId )
|
|
{
|
|
unsigned cmdSymId = cmDspSymbol(inst,kCmdPrId);
|
|
|
|
if( cmdSymId == p->storeCmdSymId || cmdSymId==p->recallCmdSymId )
|
|
{
|
|
unsigned groupSymId;
|
|
const cmChar_t* groupLabel;
|
|
const cmChar_t* presetLabel;
|
|
|
|
// get the group symbol
|
|
if((groupSymId = cmDspSymbol(inst,kGroupSymPrId)) == cmInvalidId )
|
|
return cmDspInstErr(ctx,inst,kVarNotValidDspRC,"The preset group symbol id is not set.");
|
|
|
|
// get the group label
|
|
if((groupLabel = cmSymTblLabel(ctx->stH,groupSymId)) == NULL )
|
|
return cmDspInstErr(ctx,inst,kVarNotValidDspRC,"The preset group label was not found.");
|
|
|
|
// get the preset label
|
|
if(( presetLabel = cmDspStrcz(inst,kLabelPrId)) == NULL || strlen(presetLabel)==0 )
|
|
return cmDspInstErr(ctx,inst,kVarNotValidDspRC,"The preset label was not set.");
|
|
|
|
// if this is a store command
|
|
if( cmdSymId == p->storeCmdSymId )
|
|
{
|
|
// create a new preset
|
|
if((rc = cmDspSysPresetCreate(ctx->dspH,groupLabel, presetLabel)) != kOkDspRC )
|
|
return cmDspInstErr(ctx,inst,kSubSysFailDspRC,"Preset create failed for group:'%s' preset:'%s'.",cmStringNullGuard(groupLabel),cmStringNullGuard(presetLabel));
|
|
|
|
// update the list with the new preset
|
|
rc = _cmDspPresetUpdateList(ctx, p, groupSymId );
|
|
}
|
|
else // otherwise this must be a recall command
|
|
{
|
|
rc = _cmDspPresetDoRecall(ctx,p,groupLabel, presetLabel);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmPresetClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmPreset_DC,ctx,"Preset",
|
|
NULL,
|
|
_cmDspPreset_Alloc,
|
|
_cmDspPreset_Free,
|
|
_cmDspPreset_Reset,
|
|
NULL,
|
|
_cmDspPreset_Recv,
|
|
NULL,NULL,
|
|
"Preset Manager");
|
|
|
|
return &_cmPreset_DC;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspBcastSym file_desc:"Broadcast a symbol/value to all units registered to listen for the symbol." kw:[sunit] }
|
|
|
|
enum
|
|
{
|
|
kAttrBcId,
|
|
kMsgBcId
|
|
};
|
|
|
|
cmDspClass_t _cmBcastSym_DC;
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
unsigned onSymId;
|
|
unsigned offSymId;
|
|
} cmDspBcastSym_t;
|
|
|
|
cmDspInst_t* _cmDspBcastSym_Alloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
cmDspBcastSym_t* p = cmDspInstAllocV(cmDspBcastSym_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "attr", kAttrBcId, 0, 0, kSymDsvFl | kInDsvFl | kOptArgDsvFl, "Instance which have this attribute symbol will receive the message.",
|
|
1, "msg", kMsgBcId, 0, 0, kTypeDsvMask | kInDsvFl, "Msg to broadcast.",
|
|
0 );
|
|
|
|
cmDspSetDefaultSymbol( ctx, &p->inst, kAttrBcId, cmInvalidId );
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspBcastSym_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if((rc = cmDspApplyAllDefaults(ctx,inst)) == kOkDspRC )
|
|
{
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspBcastSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
|
|
if( evt->dstVarId == kAttrBcId )
|
|
return cmDspSetEvent(ctx,inst,evt);
|
|
|
|
if( evt->dstVarId == kMsgBcId )
|
|
{
|
|
unsigned attrSymId = cmDspSymbol(inst,kAttrBcId);
|
|
if( cmDsvIsSymbol(evt->valuePtr) )
|
|
{
|
|
printf("bcast: %i %s\n",attrSymId,cmSymTblLabel(ctx->stH,cmDsvSymbol(evt->valuePtr)));
|
|
}
|
|
|
|
return cmDspSysBroadcastValue(ctx->dspH, attrSymId, evt->valuePtr );
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmBcastSymClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmBcastSym_DC,ctx,"BcastSym",
|
|
NULL,
|
|
_cmDspBcastSym_Alloc,
|
|
NULL,
|
|
_cmDspBcastSym_Reset,
|
|
NULL,
|
|
_cmDspBcastSym_Recv,
|
|
NULL,NULL,
|
|
"Set one input high and all others low.");
|
|
|
|
return &_cmBcastSym_DC;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------
|
|
//)
|
|
//( { label:cmDspSegLine file_desc:"Line segment generator." kw:[sunit] }
|
|
enum
|
|
{
|
|
kRsrcSlId,
|
|
kCmdSlId,
|
|
kTrigSlId,
|
|
kOutSlId,
|
|
};
|
|
|
|
cmDspClass_t _cmSegLineDC;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
cmDspInst_t inst;
|
|
double* x; // x[n]
|
|
unsigned n;
|
|
unsigned i; // current segment
|
|
unsigned m; // cur cnt
|
|
} cmDspSegLine_t;
|
|
|
|
cmDspInst_t* _cmDspSegLineAlloc(cmDspCtx_t* ctx, cmDspClass_t* classPtr, unsigned storeSymId, unsigned instSymId, unsigned id, unsigned va_cnt, va_list vl )
|
|
{
|
|
|
|
cmDspSegLine_t* p = cmDspInstAllocV(cmDspSegLine_t,ctx,classPtr,instSymId,id,storeSymId,va_cnt,vl,
|
|
1, "rsrc", kRsrcSlId, 0, 0, kInDsvFl | kStrzDsvFl | kReqArgDsvFl, "Name of resource array.",
|
|
1, "cmd", kCmdSlId, 0, 0, kInDsvFl | kSymDsvFl, "Command",
|
|
1, "trig", kTrigSlId, 0, 0, kInDsvFl | kTypeDsvMask, "Trigger",
|
|
1, "out", kOutSlId, 0, 0, kOutDsvFl | kDoubleDsvFl, "Output",
|
|
0 );
|
|
|
|
if( p == NULL )
|
|
return NULL;
|
|
|
|
// The array is expected to contain interleaved values:
|
|
// cnt_0, val_0, cnt_1, val_1 .... cnt_n val_n
|
|
// The 'cnt_x' values give the count of trigger values upon which the output will be 'val_x'.
|
|
|
|
if( cmDspRsrcDblArray(ctx->dspH,&p->n,&p->x,cmDspDefaultStrcz(&p->inst,kRsrcSlId),NULL) != kOkDspRC )
|
|
{
|
|
cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'SegLine' constructor resource array could not be read.");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
cmDspSetDefaultDouble( ctx, &p->inst, kOutSlId, 0.0, p->n >= 2 ? p->x[1] : 0.0 );
|
|
|
|
p->i = 0;
|
|
p->m = 0;
|
|
|
|
return &p->inst;
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspSegLineReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspSegLine_t* p = (cmDspSegLine_t*)inst;
|
|
|
|
p->i = 0;
|
|
p->m = 0;
|
|
|
|
return cmDspApplyAllDefaults(ctx,inst);
|
|
}
|
|
|
|
|
|
cmDspRC_t _cmDspSegLineRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt )
|
|
{
|
|
cmDspRC_t rc = kOkDspRC;
|
|
cmDspSegLine_t* p = (cmDspSegLine_t*)inst;
|
|
|
|
if((rc = cmDspSetEvent(ctx,inst,evt)) == kOkDspRC )
|
|
{
|
|
switch( evt->dstVarId )
|
|
{
|
|
case kTrigSlId:
|
|
{
|
|
double val = cmDspDouble(inst,kOutSlId);
|
|
if( p->i < p->n )
|
|
{
|
|
if( p->m >= p->x[p->i] )
|
|
p->i += 2;
|
|
|
|
if( p->i < p->n )
|
|
{
|
|
double x0 = p->x[p->i-2];
|
|
double y0 = p->x[p->i-1];
|
|
double x1 = p->x[p->i+0];
|
|
double y1 = p->x[p->i+1];
|
|
|
|
double dx = x1 - x0;
|
|
double dy = y1 - y0;
|
|
|
|
val = y0 + (p->m - x0) * dy / dx;
|
|
|
|
printf("i:%i m=%i x0:%f y0:%f x1:%f y1:%f : %f\n",p->i,p->m,x0,y0,x1,y1,val);
|
|
|
|
}
|
|
else
|
|
{
|
|
val = p->x[p->n-1];
|
|
}
|
|
|
|
++p->m;
|
|
|
|
}
|
|
cmDspSetDouble(ctx,inst,kOutSlId,val);
|
|
}
|
|
break;
|
|
|
|
case kCmdSlId:
|
|
p->i = 0;
|
|
p->m = 0;
|
|
break;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
cmDspClass_t* cmSegLineClassCons( cmDspCtx_t* ctx )
|
|
{
|
|
cmDspClassSetup(&_cmSegLineDC,ctx,"SegLine",
|
|
NULL,
|
|
_cmDspSegLineAlloc,
|
|
NULL,
|
|
_cmDspSegLineReset,
|
|
NULL,
|
|
_cmDspSegLineRecv,
|
|
NULL,NULL,
|
|
"SegLine");
|
|
|
|
return &_cmSegLineDC;
|
|
}
|
|
|
|
//)
|