//| Copyright: (C) 2009-2020 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. //( { 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 "cmSerialPort.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; ip = 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; iinst,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; itapCnt; ++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 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; iinst, 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; ichCnt; ++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; ichCnt; ++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; iinst, 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; ichCnt; ++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; ichCnt; ++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; ichCnt; ++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; ichCnt; ++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; ichArray[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; iccp->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; iccp->chCnt; ++i) cmDspSetBool(ctx,inst,p->nsflBaseCcId+i,p->ccp->chArray[i].nsFl); // snd the ns hz for(i=0; iccp->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; iccp->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; iinst, 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; ichCnt; ++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= 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= 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; iinst, 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; ichCnt; ++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; ichCnt; ++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; iinst, kGateBaseNnId+i, false, false ); cmDspSetDefaultDouble(ctx, &p->inst, rmsBaseNnId+i, 0.0, 0.0 ); } for(i=0; i<_cmNetNoteSelPortCnt; ++i) for(j=0; jportChCntV[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; ichCnt; ++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; ichCnt; ++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; bool allActiveFl; } 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; iinPortCnt; ++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; iinPortCnt; ++i) val += cmDspDouble( inst, kBaseOpdSoId+i ); cmDspSetDouble( ctx, inst, kOutSoId, val ); return kOkDspRC; } // var args syntax: " ... 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; bool allActiveFl = false; // 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; } if( opIdStr != NULL ) { switch( opIdStr[0] ) { case '*': fp = _cmDspScalarOpFuncMult; break; case '+': fp = _cmDspScalarOpFuncAdd; break; } // if the second character of the operator string is '$' then all input ports trigger an output if( strlen( opIdStr ) > 0 && opIdStr[1]=='$' ) allActiveFl = true; } // 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; iinst,kBaseOpdSoId+i,0.0,dfltVal[i]); p->inPortCnt = inPortCnt; p->func = fp; p->allActiveFl = allActiveFl; 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->allActiveFl ) 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; ichCnt = chCnt; p->groupCnt = groupCnt; p->gsp = cmGroupSelAlloc(ctx->cmProcCtx, NULL, 0, 0, 0 ); p->baseRmsGsId = baseRmsGsId; p->baseOutGsId = baseOutGsId; for(i=0; iinst, kBaseGateGsId, false, false ); cmDspSetDefaultDouble(ctx,&p->inst, baseRmsGsId, 0.0, 0.0 ); } for(i=0; iinst, 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; igroupCnt; ++i) { cmGroupSelGrp* gp = p->gsp->groupArray + i; if( gp->releaseFl ) { for(j=0; jchIdxCnt; ++j) cmDspSetBool(ctx,inst,p->baseOutGsId + (i*p->chCnt) + gp->chIdxArray[j], false); } if( gp->createFl ) { for(j=0; jchIdxCnt; ++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; iinst, 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; ioutChCnt; ++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; iinChCnt; ++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; ioutChCnt; ++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; ioutChCnt; ++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; iinChCnt; ++i) { assert( n == cmDspAudioBufSmpCount(ctx,inst,kBaseInRmId+i,0)); const cmSample_t* x1 = cmDspAudioBuf(ctx,inst,kBaseInRmId+i,0); if( bypassFl ) { for(j=0; jinst, 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; iarray[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; iinst, 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; ioChCnt; ++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; ioChCnt; ++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; iiChCnt; ++i) x[i] = cmDspAudioBuf(ctx,inst,p->baseInAudioNoId + i ,0); // for each valid p->map[] element for(i=0; ioChCnt; ++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; ioChCnt; ++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; ioChCnt; ++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; ioChCnt; ++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; ioChCnt; ++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; iiChCnt; ++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(; joChCnt; ++j) //{ // p->map[j] = cmInvalidIdx; // cmXfaderAllOff(p->xf[j]); //} // 5/26 if( p->iChCnt == p->oChCnt ) { for(j=0; joChCnt; ++j) if( p->map[j] == cmInvalidIdx ) cmXfaderAllOff(p->xf[j]); } else { for(; joChCnt; ++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; ioChCnt; ++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; ioChCnt; ++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; iinst, 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; idstVarId == 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; istH,symLabel); // input port - any msg in this port will generate an output from 'out' as well as the associated output port 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[idx]); } 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:cmDspIntToSym file_desc:"Send a pre-defined symbol every time a message arrives a given input port." kw:[sunit] } enum { kInItsId, kOutItsId, kBaseInItsId }; cmDspClass_t _cmIntToSym_DC; typedef struct { cmDspInst_t inst; int* intArray; unsigned* symIdArray; unsigned symIdCnt; unsigned baseIntItsId; unsigned baseOutItsId; } cmDspIntToSym_t; cmDspInst_t* _cmDspIntToSym_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 < 2 || va_cnt % 2 !=0 ) { va_end(vl1); cmDspClassErr(ctx,classPtr,kVarArgParseFailDspRC,"The 'IntToSym' constructor argument list must contain at least one int/symbol pair and all pairs must be complete."); return NULL; } unsigned symCnt = va_cnt/2; unsigned argCnt = 2 + 3*symCnt; cmDspVarArg_t args[argCnt+1]; unsigned* symIdArray = cmMemAllocZ(unsigned,symCnt); int* intArray = cmMemAllocZ(int,symCnt); unsigned baseIntItsId = kBaseInItsId + symCnt; unsigned baseOutItsId = baseIntItsId + symCnt; // setup the integer input and symbol output port arg recd cmDspArgSetup(ctx,args, "in", cmInvalidId, kInItsId, 0, 0, kInDsvFl | kIntDsvFl, "Integer input" ); cmDspArgSetup(ctx,args+1,"out", cmInvalidId, kOutItsId, 0, 0, kOutDsvFl | kSymDsvFl, "Output" ); unsigned i; for(i=0; istH,symLabel); // trigger port associated with this symbol (any msg on this port will trigger an output) cmDspArgSetup(ctx, args+kBaseInItsId+i, symLabel, cmInvalidId, kBaseInItsId+i, 0, 0, kInDsvFl | kTypeDsvMask, cmTsPrintfH(ctx->lhH,"%s Input.",symLabel) ); // this port is used to set the integer value associated with this symbol cmDspArgSetup(ctx, args+baseIntItsId+i, intLabel, cmInvalidId, baseIntItsId+i, 0, 0, kInDsvFl | kIntDsvFl, cmTsPrintfH(ctx->lhH,"Set the integer value associated with %s.",symLabel) ); // symbol output port - when ever this symbol is sent out it will go out this port as well as the 'out' port cmDspArgSetup(ctx, args+baseOutItsId+i, symLabel, cmInvalidId, baseOutItsId+i, 0, 0, kOutDsvFl | kSymDsvFl, cmTsPrintfH(ctx->lhH,"%s Output.",symLabel) ); } cmDspArgSetupNull(args + argCnt); cmDspIntToSym_t* p = cmDspInstAlloc(cmDspIntToSym_t,ctx,classPtr,args,instSymId,id,storeSymId,0,vl1); p->symIdCnt = symCnt; p->intArray = intArray; p->symIdArray = symIdArray; p->baseOutItsId = baseOutItsId; p->baseIntItsId = baseIntItsId; cmDspSetDefaultSymbol(ctx,&p->inst,kOutItsId,cmInvalidId); va_end(vl1); return &p->inst; } cmDspRC_t _cmDspIntToSym_Free(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt ) { cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst; cmMemFree(p->symIdArray); return kOkDspRC; } cmDspRC_t _cmDspIntToSym_Reset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt ) { return cmDspApplyAllDefaults(ctx,inst); } cmDspRC_t _cmDspIntToSymSendOut( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned idx ) { cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst; assert( idx < p->symIdCnt ); cmDspSetSymbol(ctx,inst,p->baseOutItsId + idx, p->symIdArray[idx]); return cmDspSetSymbol(ctx, inst, kOutItsId, p->symIdArray[ idx ]); } cmDspRC_t _cmDspIntToSym_Recv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt ) { cmDspRC_t rc = kOkDspRC; cmDspIntToSym_t* p = (cmDspIntToSym_t*)inst; // if an integer arrived at 'in' if( evt->dstVarId == kInItsId ) { cmDspSetEvent(ctx,inst,evt); unsigned i; int intVal = cmDspInt(inst,kInItsId); for(i=0; isymIdCnt; ++i) if( intVal == p->intArray[i] ) { rc = _cmDspIntToSymSendOut( ctx, inst, i ); break; } } else { // if a msg of any type is recieved on an input port - send out the associated symbol if( kBaseInItsId <= evt->dstVarId && evt->dstVarId < kBaseInItsId + p->symIdCnt ) { _cmDspIntToSymSendOut( ctx, inst, evt->dstVarId - kBaseInItsId ); } else // if this is a new interger value for this symbol if( p->baseIntItsId <= evt->dstVarId && evt->dstVarId < p->baseIntItsId + p->symIdCnt ) { cmDspSetEvent(ctx,inst,evt); p->intArray[ evt->dstVarId - p->baseIntItsId ] = cmDspInt( inst, evt->dstVarId ); } } return rc; } cmDspClass_t* cmIntToSymClassCons( cmDspCtx_t* ctx ) { cmDspClassSetup(&_cmIntToSym_DC,ctx,"IntToSym", NULL, _cmDspIntToSym_Alloc, _cmDspIntToSym_Free, _cmDspIntToSym_Reset, NULL, _cmDspIntToSym_Recv, NULL,NULL, "If a message of any kind is received on a port then send the symbol associated with the port."); return &_cmIntToSym_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; iinst, 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; ioChCnt; ++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 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. Note that this channel is // sent "prospectively" - possibly long before the associated // gate[ch] is raised - in order to switch parameter routers away // from the newly active channel and a currently in-active channel. // 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; iinst, 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; ichCnt; ++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; ichCnt; ++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; ichCnt; ++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; } //)