#include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwMem.h" #include "cwObject.h" #include "cwAudioFile.h" #include "cwVectOps.h" #include "cwMtx.h" #include "cwDspTypes.h" // real_t, sample_t #include "cwFlow.h" #include "cwFlowTypes.h" #include "cwFlowProc.h" #include "cwFile.h" #include "cwMath.h" #include "cwDsp.h" #include "cwAudioTransforms.h" #include "cwDspTransforms.h" namespace cw { namespace flow { //------------------------------------------------------------------------------------------------------------------ // // Template // namespace template_proc { typedef struct { } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // Custom create code goes here return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; // Custom clean-up code goes here mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; //inst_t* inst = (inst_t*)ctx->userPtr; return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // balance // namespace balance { enum { kInPId, kOutPId, kInvOutPId }; typedef struct { real_t value; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; real_t in_value = 0.5; ctx->userPtr = mem::allocZ(); if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", in_value )) != kOkRC ) goto errLabel; if((rc = var_register_and_set( ctx, kAnyChIdx, kOutPId, "out", in_value, kInvOutPId, "inv_out", (real_t)(1.0-in_value) )) != kOkRC ) { goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { mem::release( ctx->userPtr ); return kOkRC; } rc_t value( instance_t* ctx, variable_t* var ) { return kOkRC; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)(ctx->userPtr); real_t value = 1; var_get(ctx, kInPId, kAnyChIdx, value); var_set(ctx, kOutPId, kAnyChIdx, value); var_set(ctx, kInvOutPId, kAnyChIdx, (real_t)(1.0 - value) ); if( inst->value != value ) { inst->value = value; } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_in // namespace audio_in { enum { kDevLabelPId, kOutPId }; typedef struct { const char* dev_label; external_device_t* ext_dev; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // Register variable and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, kDevLabelPId, "dev_label", inst->dev_label )) != kOkRC ) { goto errLabel; } if((inst->ext_dev = external_device_find( ctx->ctx, inst->dev_label, kAudioDevTypeId, kInFl )) == nullptr ) { rc = cwLogError(kOpFailRC,"The audio input device description '%s' could not be found.", cwStringNullGuard(inst->dev_label)); goto errLabel; } // create one output audio buffer rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, inst->ext_dev->u.a.abuf->srate, inst->ext_dev->u.a.abuf->chN, ctx->ctx->framesPerCycle ); errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; abuf_t* abuf = nullptr; // verify that a source buffer exists if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC ) { rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",ctx->label); } else { unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, abuf->chN ); unsigned frameN = std::min(inst->ext_dev->u.a.abuf->frameN, abuf->frameN ); memcpy(abuf->buf,inst->ext_dev->u.a.abuf->buf, frameN*chN*sizeof(sample_t)); } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_out // namespace audio_out { enum { kInPId, kDevLabelPId, }; typedef struct { const char* dev_label; external_device_t* ext_dev; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; // inst_t* inst = mem::allocZ(); // const abuf_t* src_abuf = nullptr; ctx->userPtr = inst; // Register variables and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, kDevLabelPId, "dev_label", inst->dev_label, kInPId, "in", src_abuf)) != kOkRC ) { goto errLabel; } if((inst->ext_dev = external_device_find( ctx->ctx, inst->dev_label, kAudioDevTypeId, kOutFl )) == nullptr ) { rc = cwLogError(kOpFailRC,"The audio output device description '%s' could not be found.", cwStringNullGuard(inst->dev_label)); goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* src_abuf = nullptr; if((rc = var_get(ctx,kInPId,kAnyChIdx,src_abuf)) != kOkRC ) rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label); else { unsigned chN = std::min(inst->ext_dev->u.a.abuf->chN, src_abuf->chN); unsigned frameN = std::min(inst->ext_dev->u.a.abuf->frameN, src_abuf->frameN); unsigned n = chN * frameN; for(unsigned i=0; iext_dev->u.a.abuf->buf[i] += src_abuf->buf[i]; } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // AudioFileIn // namespace audioFileIn { enum { kFnamePId, kEofFlPId, kOutPId }; typedef struct { audiofile::handle_t afH; bool eofFl; const char* filename; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; audiofile::info_t info; inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // Register variable and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, kFnamePId, "fname", inst->filename, kEofFlPId, "eofFl", inst->eofFl )) != kOkRC ) { goto errLabel; } // open the audio file if((rc = audiofile::open(inst->afH,inst->filename,&info)) != kOkRC ) { rc = cwLogError(kInvalidArgRC,"The audio file '%s' could not be opened.",inst->filename); goto errLabel; } cwLogInfo("Audio '%s' srate:%f chs:%i frames:%i %f seconds.",inst->filename,info.srate,info.chCnt,info.frameCnt, info.frameCnt/info.srate ); // create one output audio buffer - with the same configuration as the source audio file rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, info.srate, info.chCnt, ctx->ctx->framesPerCycle ); errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; if((rc = audiofile::close(inst->afH)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The close failed on the audio file '%s'.", cwStringNullGuard(inst->filename) ); } mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; unsigned actualFrameN = 0; inst_t* inst = (inst_t*)ctx->userPtr; abuf_t* abuf = nullptr; // verify that a source buffer exists if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC ) { rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid audio output buffer.",ctx->label); } else { sample_t* chBuf[ abuf->chN ]; for(unsigned i=0; ichN; ++i) chBuf[i] = abuf->buf + (i*abuf->frameN); rc = readFloat(inst->afH, abuf->frameN, 0, abuf->chN, chBuf, &actualFrameN ); if( inst->eofFl && actualFrameN == 0) rc = kEofRC; } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // AudioFileOut // namespace audioFileOut { enum { kInPId, kFnamePId, kBitsPId }; typedef struct { audiofile::handle_t afH; const char* filename; unsigned durSmpN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; // unsigned audioFileBits = 0; // set audio file sample format to 'float32'. inst_t* inst = mem::allocZ(); // const abuf_t* src_abuf = nullptr; ctx->userPtr = inst; // Register variables and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, kFnamePId, "fname", inst->filename, kBitsPId, "bits", audioFileBits, kInPId, "in", src_abuf)) != kOkRC ) { goto errLabel; } // create the audio file with the same channel count as the incoming signal if((rc = audiofile::create( inst->afH, inst->filename, src_abuf->srate, audioFileBits, src_abuf->chN)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The audio file create failed on '%s'.",cwStringNullGuard(inst->filename)); goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; // close the audio file if((rc = audiofile::close( inst->afH )) != kOkRC ) { rc = cwLogError(rc,"Close failed on the audio output file '%s'.",inst->filename); goto errLabel; } mem::release(inst); errLabel: return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* src_abuf = nullptr; if((rc = var_get(ctx,kInPId,kAnyChIdx,src_abuf)) != kOkRC ) rc = cwLogError(kInvalidStateRC,"The audio file instance '%s' does not have a valid input connection.",ctx->label); else { sample_t* chBuf[ src_abuf->chN ]; for(unsigned i=0; ichN; ++i) chBuf[i] = src_abuf->buf + (i*src_abuf->frameN); if((rc = audiofile::writeFloat(inst->afH, src_abuf->frameN, src_abuf->chN, chBuf )) != kOkRC ) rc = cwLogError(rc,"Audio file write failed on instance: '%s'.", ctx->label ); // print a minutes counter inst->durSmpN += src_abuf->frameN; if( inst->durSmpN % ((unsigned)src_abuf->srate*60) == 0 ) printf("audio file out: %5.1f min\n", inst->durSmpN/(src_abuf->srate*60)); } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_gain // namespace audio_gain { enum { kInPId, kGainPId, kOutPId }; typedef struct inst_str { unsigned n; real_t vgain; real_t gain; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // ctx->userPtr = mem::allocZ(); // get the source audio buffer if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) goto errLabel; // register the gain for(unsigned i=0; ichN; ++i) if((rc = var_register( ctx, i, kGainPId, "gain" )) != kOkRC ) goto errLabel; // create the output audio buffer rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: return rc; } rc_t destroy( instance_t* ctx ) { inst_t* inst = (inst_t*)(ctx->userPtr); mem::release(inst); return kOkRC; } rc_t value( instance_t* ctx, variable_t* var ) { real_t value = 0; inst_t* inst = (inst_t*)ctx->userPtr; var_get(ctx,kGainPId,0,value); if( inst->vgain != value ) { inst->vgain = value; //printf("VALUE GAIN: %s %s : %f\n", ctx->label, var->label, value ); } return kOkRC; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; inst_t* inst = (inst_t*)(ctx->userPtr); // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each channel for(unsigned i=0; ichN; ++i) { sample_t* isig = ibuf->buf + i*ibuf->frameN; sample_t* osig = obuf->buf + i*obuf->frameN; sample_t gain = 1; var_get(ctx,kGainPId,i,gain); // apply the gain for(unsigned j=0; jframeN; ++j) osig[j] = gain * isig[j]; if( i==0 && gain != inst->gain ) { inst->gain = gain; //printf("EXEC GAIN: %s %f\n",ctx->label,gain); //instance_print(ctx); } } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_split // namespace audio_split { enum { kInPId, kSelectPId, kGainPId, kOutPId, }; typedef struct { bool* chSelMap; // [ inChCnt ] selected channel map unsigned outChN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // get the source audio buffer if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) goto errLabel; if( abuf->chN ) { unsigned selChN = 0; inst->chSelMap = mem::allocZ(abuf->chN); if((rc = var_channel_count(ctx,"select",selChN)) != kOkRC ) goto errLabel; // register the gain for(unsigned i=0; ichN; ++i) { if( i < selChN ) if((rc = var_register_and_get( ctx, i, kSelectPId, "select", inst->chSelMap[i] )) != kOkRC ) goto errLabel; if( inst->chSelMap[i] ) { // register an output gain control if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) goto errLabel; // count the number of selected channels to determine the count of output channels inst->outChN += 1; } } // create the output audio buffer if( inst->outChN == 0 ) cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); else rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; mem::release(inst->chSelMap); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; inst_t* inst = (inst_t*)ctx->userPtr; unsigned outChIdx = 0; if( inst->outChN ) { // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each channel for(unsigned i=0; ichN && outChIdxchN; ++i) if( inst->chSelMap[i] ) { sample_t* isig = ibuf->buf + i * ibuf->frameN; sample_t* osig = obuf->buf + outChIdx * obuf->frameN; sample_t gain = 1; var_get(ctx,kGainPId,outChIdx,gain); // apply the gain for(unsigned j=0; jframeN; ++j) osig[j] = gain * isig[j]; outChIdx += 1; } } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_duplicate // namespace audio_duplicate { enum { kInPId, kDuplicatePId, kGainPId, kOutPId, }; typedef struct { unsigned* chDuplMap; // [ inChN ] duplicate channel map unsigned outChN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // get the source audio buffer if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) goto errLabel; if( abuf->chN ) { inst->chDuplMap = mem::allocZ(abuf->chN); // register the gain for(unsigned i=0; ichN; ++i) { if((rc = var_register_and_get( ctx, i, kDuplicatePId, "duplicate", inst->chDuplMap[i] )) != kOkRC ) goto errLabel; if( inst->chDuplMap[i] ) { // register an input gain control if((rc = var_register( ctx, inst->outChN, kGainPId, "gain")) != kOkRC ) goto errLabel; // count the number of selected channels to determine the count of output channels inst->outChN += inst->chDuplMap[i]; } } // create the output audio buffer if( inst->outChN == 0 ) cwLogWarning("The audio split instance '%s' has no selected channels.",ctx->label); else rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, inst->outChN, abuf->frameN ); } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; mem::release(inst->chDuplMap); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; inst_t* inst = (inst_t*)ctx->userPtr; unsigned outChIdx = 0; if( inst->outChN ) { // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each input channel for(unsigned i=0; ichN && outChIdxchN; ++i) { sample_t* isig = ibuf->buf + i * ibuf->frameN; sample_t gain = 1; var_get(ctx,kGainPId,i,gain); for(unsigned j=0; jchDuplMap[i]; ++j ) { sample_t* osig = obuf->buf + j * obuf->frameN; // apply the gain for(unsigned k=0; kframeN; ++k) osig[k] = gain * isig[k]; outChIdx += 1; } } } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_merge // namespace audio_merge { enum { kGainPId, kOutPId, kInBasePId, }; typedef struct { unsigned srcN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; unsigned outChN = 0; unsigned frameN = 0; srate_t srate = 0; inst_t* inst = mem::allocZ(); ctx->userPtr = inst; for(unsigned i=0; 1; ++i) { const abuf_t* abuf = nullptr; // char label[32]; snprintf(label,31,"in%i",i); label[31] = 0; // TODO: allow non-contiguous source labels // the source labels must be contiguous if( !var_has_value( ctx, label, kAnyChIdx ) ) break; // get the source audio buffer if((rc = var_register_and_get(ctx, kAnyChIdx,kInBasePId+i,label,abuf )) != kOkRC ) { goto errLabel; } if( i == 0 ) { frameN = abuf->frameN; srate = abuf->srate; } else { // TODO: check srate and frameN are same as first src } inst->srcN += 1; outChN += abuf->chN; } //outChN = abuf0->chN + abuf1->chN; // register the gain for(unsigned i=0; iuserPtr; mem::release(inst); return kOkRC; } rc_t value( instance_t* ctx, variable_t* var ) { return kOkRC; } unsigned _exec( instance_t* ctx, const abuf_t* ibuf, abuf_t* obuf, unsigned outChIdx ) { // for each channel for(unsigned i=0; ichN && outChIdxchN; ++i) { sample_t* isig = ibuf->buf + i * ibuf->frameN; sample_t* osig = obuf->buf + outChIdx * obuf->frameN; sample_t gain = 1; var_get(ctx,kGainPId,outChIdx,gain); // apply the gain for(unsigned j=0; jframeN; ++j) osig[j] = gain * isig[j]; outChIdx += 1; } return outChIdx; } /* rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* ibuf0 = nullptr; const abuf_t* ibuf1 = nullptr; abuf_t* obuf = nullptr; unsigned oChIdx = 0; if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) goto errLabel; if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) goto errLabel; if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; oChIdx = _exec( ctx, ibuf0, obuf, oChIdx ); oChIdx = _exec( ctx, ibuf1, obuf, oChIdx ); assert( oChIdx == obuf->chN ); errLabel: return rc; } */ rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; abuf_t* obuf = nullptr; unsigned oChIdx = 0; if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; for(unsigned i=0; isrcN; ++i) { const abuf_t* ibuf = nullptr; if((rc = var_get(ctx,kInBasePId+i, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; oChIdx = _exec( ctx, ibuf, obuf, oChIdx ); } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // audio_mix // namespace audio_mix { enum { kIn0PId, kIn1PId, kGain0PId, kGain1PId, kOutPId, }; typedef struct { } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* abuf0 = nullptr; // const abuf_t* abuf1 = nullptr; unsigned outChN = 0; // get the source audio buffer if((rc = var_register_and_get(ctx, kAnyChIdx, kIn0PId,"in0",abuf0, kIn1PId,"in1",abuf1 )) != kOkRC ) { goto errLabel; } assert( abuf0->frameN == abuf1->frameN ); outChN = std::max(abuf0->chN, abuf1->chN); // register the gain var_register( ctx, kAnyChIdx, kGain0PId, "gain0" ); var_register( ctx, kAnyChIdx, kGain1PId, "gain1" ); // create the output audio buffer rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf0->srate, outChN, abuf0->frameN ); errLabel: return rc; } rc_t destroy( instance_t* ctx ) { return kOkRC; } rc_t value( instance_t* ctx, variable_t* var ) { return kOkRC; } rc_t _mix( instance_t* ctx, unsigned inPId, unsigned gainPId, abuf_t* obuf ) { rc_t rc = kOkRC; const abuf_t* ibuf = nullptr; if((rc = var_get(ctx, inPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; if(rc == kOkRC ) { unsigned chN = std::min(ibuf->chN, obuf->chN ); for(unsigned i=0; ibuf + i*ibuf->frameN; sample_t* osig = obuf->buf + i*obuf->frameN; real_t gain = 1; if((rc = var_get(ctx, gainPId, i, gain)) != kOkRC ) goto errLabel; for(unsigned j=0; jframeN; ++j) osig[j] += gain * isig[j]; } } errLabel: return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; abuf_t* obuf = nullptr; const abuf_t* ibuf0 = nullptr; const abuf_t* ibuf1 = nullptr; if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; if((rc = var_get(ctx,kIn0PId, kAnyChIdx, ibuf0 )) != kOkRC ) goto errLabel; if((rc = var_get(ctx,kIn1PId, kAnyChIdx, ibuf1 )) != kOkRC ) goto errLabel; _mix( ctx, kIn0PId, kGain0PId, obuf ); _mix( ctx, kIn1PId, kGain1PId, obuf ); errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // sine_tone // namespace sine_tone { enum { kSratePId, kChCntPid, kFreqHzPId, kGainPId, kOutPId }; typedef struct { real_t *phaseA; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = mem::allocZ(); srate_t srate = 0; unsigned chCnt = 0; real_t gain; real_t hz; ctx->userPtr = inst; // Register variables and get their current value if((rc = var_register_and_get( ctx, kAnyChIdx, kChCntPid, "chCnt", chCnt)) != kOkRC ) { goto errLabel; } // register each oscillator variable for(unsigned i=0; ictx->framesPerCycle ); inst->phaseA = mem::allocZ( chCnt ); errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; abuf_t* abuf = nullptr; // get the output signal buffer if((rc = var_get(ctx,kOutPId,kAnyChIdx,abuf)) != kOkRC ) { rc = cwLogError(kInvalidStateRC,"The Sine Tone instance '%s' does not have a valid audio output buffer.",ctx->label); } else { for(unsigned i=0; ichN; ++i) { real_t gain = val_get( ctx, kGainPId, i ); real_t hz = val_get( ctx, kFreqHzPId, i ); srate_t srate = val_get( ctx, kSratePId, i ); sample_t* v = abuf->buf + (i*abuf->frameN); for(unsigned j=0; jframeN; ++j) v[j] = (sample_t)(gain * sin( inst->phaseA[i] + (2.0 * M_PI * j * hz/srate))); inst->phaseA[i] += 2.0 * M_PI * abuf->frameN * hz/srate; } } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // Phase Vocoder (Analysis) // namespace pv_analysis { typedef struct dsp::pv_anl::obj_str pv_t; enum { kInPId, kMaxWndSmpNPId, kWndSmpNPId, kHopSmpNPId, kHzFlPId, kOutPId }; typedef struct { pv_t** pvA; // pvA[ srcBuf.chN ] unsigned pvN; unsigned maxWndSmpN; unsigned wndSmpN; unsigned hopSmpN; bool hzFl; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // unsigned flags = 0; inst_t* inst = mem::allocZ(); ctx->userPtr = inst; if((rc = var_register_and_get( ctx, kAnyChIdx, kInPId, "in", srcBuf, kMaxWndSmpNPId, "maxWndSmpN", inst->maxWndSmpN, kWndSmpNPId, "wndSmpN", inst->wndSmpN, kHopSmpNPId, "hopSmpN", inst->hopSmpN, kHzFlPId, "hzFl", inst->hzFl )) != kOkRC ) { goto errLabel; } else { flags = inst->hzFl ? dsp::pv_anl::kCalcHzPvaFl : dsp::pv_anl::kNoCalcHzPvaFl; inst->pvN = srcBuf->chN; inst->pvA = mem::allocZ( inst->pvN ); // allocate pv channel array const sample_t* magV[ srcBuf->chN ]; const sample_t* phsV[ srcBuf->chN ]; const sample_t* hzV[ srcBuf->chN ]; // create a pv anlaysis object for each input channel for(unsigned i=0; ichN; ++i) { if((rc = create( inst->pvA[i], ctx->ctx->framesPerCycle, srcBuf->srate, inst->maxWndSmpN, inst->wndSmpN, inst->hopSmpN, flags )) != kOkRC ) { rc = cwLogError(kOpFailRC,"The PV analysis object create failed on the instance '%s'.",ctx->label); goto errLabel; } magV[i] = inst->pvA[i]->magV; phsV[i] = inst->pvA[i]->phsV; hzV[i] = inst->pvA[i]->hzV; } if((rc = var_register( ctx, kAnyChIdx, kInPId, "in" )) != kOkRC ) goto errLabel; // create the fbuf 'out' rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, inst->pvA[0]->maxBinCnt, srcBuf->chN, inst->pvA[0]->binCnt, inst->pvA[0]->hopSmpCnt, magV, phsV, hzV ); } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; ipvN; ++i) destroy(inst->pvA[i]); mem::release(inst->pvA); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* srcBuf = nullptr; fbuf_t* dstBuf = nullptr; // verify that a source buffer exists if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; } // verify that the dst buffer exits if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid output.",ctx->label); goto errLabel; } // for each input channel for(unsigned i=0; ichN; ++i) { dstBuf->readyFlV[i] = false; // call the PV analysis processor if( dsp::pv_anl::exec( inst->pvA[i], srcBuf->buf + i*srcBuf->frameN, srcBuf->frameN ) ) { // rescale the frequency domain magnitude vop::mul(dstBuf->magV[i], dstBuf->binN_V[i]/2, dstBuf->binN_V[i]); dstBuf->readyFlV[i] = true; } } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // Phase Vocoder (Synthesis) // namespace pv_synthesis { typedef struct dsp::pv_syn::obj_str pv_t; enum { kInPId, kOutPId }; typedef struct { pv_t** pvA; // pvA[ srcBuf.chN ] unsigned pvN; unsigned wndSmpN; // unsigned hopSmpN; // bool hzFl; // } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const fbuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; if((rc = var_register_and_get( ctx, kAnyChIdx,kInPId, "in", srcBuf)) != kOkRC ) { goto errLabel; } else { // allocate pv channel array inst->pvN = srcBuf->chN; inst->pvA = mem::allocZ( inst->pvN ); // create a pv anlaysis object for each input channel for(unsigned i=0; ichN; ++i) { unsigned wndSmpN = (srcBuf->binN_V[i]-1)*2; if((rc = create( inst->pvA[i], ctx->ctx->framesPerCycle, srcBuf->srate, wndSmpN, srcBuf->hopSmpN_V[i] )) != kOkRC ) { rc = cwLogError(kOpFailRC,"The PV synthesis object create failed on the instance '%s'.",ctx->label); goto errLabel; } } if((rc = var_register( ctx, kAnyChIdx, kInPId, "in" )) != kOkRC ) goto errLabel; // create the abuf 'out' rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, ctx->ctx->framesPerCycle ); } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; ipvN; ++i) destroy(inst->pvA[i]); mem::release(inst->pvA); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const fbuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; for(unsigned i=0; ichN; ++i) { if( srcBuf->readyFlV[i] ) dsp::pv_syn::exec( inst->pvA[i], srcBuf->magV[i], srcBuf->phsV[i] ); const sample_t* ola_out = dsp::ola::execOut(inst->pvA[i]->ola); if( ola_out != nullptr ) abuf_set_channel( dstBuf, i, ola_out, inst->pvA[i]->ola->procSmpCnt ); //abuf_set_channel( dstBuf, i, inst->pvA[i]->ola->outV, dstBuf->frameN ); } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // Spec Dist // namespace spec_dist { typedef struct dsp::spec_dist::obj_str spec_dist_t; enum { kInPId, kCeilingPId, kExpoPId, kThreshPId, kUprSlopePId, kLwrSlopePId, kMixPId, kOutPId, }; typedef struct { spec_dist_t** sdA; unsigned sdN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const fbuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // verify that a source buffer exists if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; } else { // allocate pv channel array inst->sdN = srcBuf->chN; inst->sdA = mem::allocZ( inst->sdN ); const sample_t* magV[ srcBuf->chN ]; const sample_t* phsV[ srcBuf->chN ]; const sample_t* hzV[ srcBuf->chN ]; //if((rc = var_register(ctx, kAnyChIdx, kInPId, "in")) != kOkRC ) // goto errLabel; // create a spec_dist object for each input channel for(unsigned i=0; ichN; ++i) { if((rc = create( inst->sdA[i], srcBuf->binN_V[i] )) != kOkRC ) { rc = cwLogError(kOpFailRC,"The 'spec dist' object create failed on the instance '%s'.",ctx->label); goto errLabel; } // setup the output buffer pointers magV[i] = inst->sdA[i]->outMagV; phsV[i] = inst->sdA[i]->outPhsV; hzV[i] = nullptr; spec_dist_t* sd = inst->sdA[i]; if((rc = var_register_and_get( ctx, i, kCeilingPId, "ceiling", sd->ceiling, kExpoPId, "expo", sd->expo, kThreshPId, "thresh", sd->thresh, kUprSlopePId, "upr", sd->uprSlope, kLwrSlopePId, "lwr", sd->lwrSlope, kMixPId, "mix", sd->mix )) != kOkRC ) { goto errLabel; } } // create the output buffer if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->maxBinN, srcBuf->chN, srcBuf->binN_V, srcBuf->hopSmpN_V, magV, phsV, hzV )) != kOkRC ) goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; isdN; ++i) destroy(inst->sdA[i]); mem::release(inst->sdA); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; if( var->chIdx != kAnyChIdx && var->chIdx < inst->sdN ) { double val = 0; spec_dist_t* sd = inst->sdA[ var->chIdx ]; switch( var->vid ) { case kCeilingPId: rc = var_get( var, val ); sd->ceiling = val; break; case kExpoPId: rc = var_get( var, val ); sd->expo = val; break; case kThreshPId: rc = var_get( var, val ); sd->thresh = val; break; case kUprSlopePId: rc = var_get( var, val ); sd->uprSlope = val; break; case kLwrSlopePId: rc = var_get( var, val ); sd->lwrSlope = val; break; case kMixPId: rc = var_get( var, val ); sd->mix = val; break; default: cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); } //printf("sd: ceil:%f expo:%f thresh:%f upr:%f lwr:%f mix:%f : rc:%i val:%f\n", // sd->ceiling, sd->expo, sd->thresh, sd->uprSlope, sd->lwrSlope, sd->mix, rc, val ); } return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const fbuf_t* srcBuf = nullptr; fbuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->sdN); for(unsigned i=0; ireadyFlV[i] = false; if( srcBuf->readyFlV[i] ) { dsp::spec_dist::exec( inst->sdA[i], srcBuf->magV[i], srcBuf->phsV[i], srcBuf->binN_V[i] ); dstBuf->readyFlV[i] = true; //If == 0 ) // printf("%f %f\n", vop::sum(srcBuf->magV[i],srcBuf->binN), vop::sum(dstBuf->magV[i], dstBuf->binN) ); } } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // Compressor // namespace compressor { enum { kInPId, kBypassPId, kInGainPId, kThreshPId, kRatioPId, kAtkMsPId, kRlsMsPId, kWndMsPId, kMaxWndMsPId, kOutGainPId, kOutPId, kEnvPId }; typedef dsp::compressor::obj_t compressor_t; typedef struct { compressor_t** cmpA; unsigned cmpN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // verify that a source buffer exists if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; } else { // allocate pv channel array inst->cmpN = srcBuf->chN; inst->cmpA = mem::allocZ( inst->cmpN ); // create a compressor object for each input channel for(unsigned i=0; ichN; ++i) { real_t igain, maxWnd_ms, wnd_ms, thresh, ratio, atk_ms, rls_ms, ogain; bool bypassFl; // get the compressor variable values if((rc = var_register_and_get( ctx, i, kBypassPId, "bypass", bypassFl, kInGainPId, "igain", igain, kThreshPId, "thresh", thresh, kRatioPId, "ratio", ratio, kAtkMsPId, "atk_ms", atk_ms, kRlsMsPId, "rls_ms", rls_ms, kWndMsPId, "wnd_ms", wnd_ms, kMaxWndMsPId, "maxWnd_ms", maxWnd_ms, kOutGainPId, "ogain", ogain )) != kOkRC ) { goto errLabel; } // create the compressor instance if((rc = dsp::compressor::create( inst->cmpA[i], srcBuf->srate, srcBuf->frameN, igain, maxWnd_ms, wnd_ms, thresh, ratio, atk_ms, rls_ms, ogain, bypassFl)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The 'compressor' object create failed on the instance '%s'.",ctx->label); goto errLabel; } } // create the output audio buffer if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; icmpN; ++i) destroy(inst->cmpA[i]); mem::release(inst->cmpA); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; real_t tmp; if( var->chIdx != kAnyChIdx && var->chIdx < inst->cmpN ) { compressor_t* c = inst->cmpA[ var->chIdx ]; switch( var->vid ) { case kBypassPId: rc = var_get( var, tmp ); c->bypassFl=tmp; break; case kInGainPId: rc = var_get( var, tmp ); c->inGain=tmp; break; case kOutGainPId: rc = var_get( var, tmp ); c->outGain=tmp; break; case kRatioPId: rc = var_get( var, tmp ); c->ratio_num=tmp; break; case kThreshPId: rc = var_get( var, tmp ); c->threshDb=tmp; break; case kAtkMsPId: rc = var_get( var, tmp ); dsp::compressor::set_attack_ms(c, tmp ); break; case kRlsMsPId: rc = var_get( var, tmp ); dsp::compressor::set_release_ms(c, tmp ); break; case kWndMsPId: rc = var_get( var, tmp ); dsp::compressor::set_rms_wnd_ms(c, tmp ); break; case kMaxWndMsPId: break; default: cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); } //printf("cmp byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n", // c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp); } return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->cmpN); for(unsigned i=0; icmpA[i], srcBuf->buf + i*srcBuf->frameN, dstBuf->buf + i*srcBuf->frameN, srcBuf->frameN ); } errLabel: return rc; } rc_t report( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; icmpN; ++i) { compressor_t* c = inst->cmpA[i]; cwLogInfo("%s ch:%i : sr:%f bypass:%i procSmpN:%i igain:%f threshdb:%f ratio:%f atkSmp:%i rlsSmp:%i ogain:%f rmsWndN:%i maxRmsWndN%i", ctx->label,i,c->srate,c->bypassFl,c->procSmpCnt,c->inGain,c->threshDb,c->ratio_num,c->atkSmp,c->rlsSmp,c->outGain,c->rmsWndCnt,c->rmsWndAllocCnt ); } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = report }; } //------------------------------------------------------------------------------------------------------------------ // // Limiter // namespace limiter { enum { kInPId, kBypassPId, kInGainPId, kThreshPId, kOutGainPId, kOutPId, }; typedef dsp::limiter::obj_t limiter_t; typedef struct { limiter_t** limA; unsigned limN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // verify that a source buffer exists if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; } else { // allocate pv channel array inst->limN = srcBuf->chN; inst->limA = mem::allocZ( inst->limN ); // create a limiter object for each input channel for(unsigned i=0; ichN; ++i) { real_t igain, thresh, ogain; bool bypassFl; // get the limiter variable values if((rc = var_register_and_get( ctx, i, kBypassPId, "bypass", bypassFl, kInGainPId, "igain", igain, kThreshPId, "thresh", thresh, kOutGainPId, "ogain", ogain )) != kOkRC ) { goto errLabel; } // create the limiter instance if((rc = dsp::limiter::create( inst->limA[i], srcBuf->srate, srcBuf->frameN, igain, thresh, ogain, bypassFl)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The 'limiter' object create failed on the instance '%s'.",ctx->label); goto errLabel; } } // create the output audio buffer if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; ilimN; ++i) destroy(inst->limA[i]); mem::release(inst->limA); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; real_t rtmp; bool btmp; if( var->chIdx != kAnyChIdx && var->chIdx < inst->limN ) { limiter_t* c = inst->limA[ var->chIdx ]; switch( var->vid ) { case kBypassPId: rc = var_get( var, btmp ); c->bypassFl=btmp; break; case kInGainPId: rc = var_get( var, rtmp ); c->igain=rtmp; break; case kOutGainPId: rc = var_get( var, rtmp ); c->ogain=rtmp; break; case kThreshPId: rc = var_get( var, rtmp ); c->thresh=rtmp; break; default: cwLogWarning("Unhandled variable id '%i' on instance: %s.", var->vid, ctx->label ); } //printf("lim byp:%i igain:%f ogain:%f rat:%f thresh:%f atk:%i rls:%i wnd:%i : rc:%i val:%f\n", // c->bypassFl, c->inGain, c->outGain,c->ratio_num,c->threshDb,c->atkSmp,c->rlsSmp,c->rmsWndCnt,rc,tmp); } return rc; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->limN); for(unsigned i=0; ilimA[i], srcBuf->buf + i*srcBuf->frameN, dstBuf->buf + i*srcBuf->frameN, srcBuf->frameN ); } errLabel: return rc; } rc_t report( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; ilimN; ++i) { limiter_t* c = inst->limA[i]; cwLogInfo("%s ch:%i : bypass:%i procSmpN:%i igain:%f threshdb:%f ogain:%f", ctx->label,i,c->bypassFl,c->procSmpCnt,c->igain,c->thresh,c->ogain ); } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = report }; } //------------------------------------------------------------------------------------------------------------------ // // audio_delay // namespace audio_delay { enum { kInPId, kMaxDelayMsPId, kDelayMsPId, kOutPId }; typedef struct inst_str { abuf_t* delayBuf; // delayBuf->buf[ maxDelayFrameN ] unsigned maxDelayFrameN; // length of the delay unsigned* cntV; // cntV[ chN ] per channel delay unsigned* idxV; // idxV[ chN ] per channel i/o idx } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* abuf = nullptr; // inst_t* inst = mem::allocZ(); real_t delayMs = 0; real_t maxDelayMs = 0; ctx->userPtr = inst; // get the source audio buffer if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",abuf )) != kOkRC ) goto errLabel; inst->cntV = mem::allocZ(abuf->chN); inst->idxV = mem::allocZ(abuf->chN); // register the gain for(unsigned i=0; ichN; ++i) { if((rc = var_register_and_get( ctx, i, kMaxDelayMsPId, "maxDelayMs", maxDelayMs, kDelayMsPId, "delayMs", delayMs)) != kOkRC ) { goto errLabel; } if( delayMs > maxDelayMs ) { cwLogWarning("'delayMs' (%i) is being reduced to 'maxDelayMs' (%i) on the delay instance:%s.",delayMs,maxDelayMs,ctx->label); delayMs = maxDelayMs; } inst->maxDelayFrameN = std::max(inst->maxDelayFrameN, (unsigned)(fabs(maxDelayMs) * abuf->srate / 1000.0) ); inst->cntV[i] = (unsigned)(fabs(delayMs) * abuf->srate / 1000.0); } inst->delayBuf = abuf_create( abuf->srate, abuf->chN, inst->maxDelayFrameN ); // create the output audio buffer rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, abuf->srate, abuf->chN, abuf->frameN ); errLabel: return rc; } rc_t destroy( instance_t* ctx ) { inst_t* inst = (inst_t*)ctx->userPtr; mem::release(inst->cntV); mem::release(inst->idxV); abuf_destroy(inst->delayBuf); mem::release(inst); return kOkRC; } rc_t value( instance_t* ctx, variable_t* var ) { return kOkRC; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* ibuf = nullptr; abuf_t* obuf = nullptr; abuf_t* dbuf = inst->delayBuf; // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, ibuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, obuf)) != kOkRC ) goto errLabel; // for each channel for(unsigned i=0; ichN; ++i) { sample_t* isig = ibuf->buf + i*ibuf->frameN; sample_t* osig = obuf->buf + i*obuf->frameN; sample_t* dsig = dbuf->buf + i*dbuf->frameN; unsigned di = inst->idxV[i]; // if the delay is set to zero samples if( inst->cntV[i] == 0 ) memcpy(osig,isig,ibuf->frameN * sizeof(sample_t)); else { // otherwise the delay is non-zero positive sample count for(unsigned j=0; jframeN; ++j) { osig[j] = dsig[di]; // read delay output dsig[di] = isig[j]; // set delay input di = (di+1) % inst->cntV[i]; // update the delay index } } // store the delay index for the next cycle inst->idxV[i] = di; } errLabel: return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = nullptr }; } //------------------------------------------------------------------------------------------------------------------ // // DC Filter // namespace dc_filter { enum { kInPId, kBypassPId, kGainPId, kOutPId, }; typedef dsp::dc_filter::obj_t dc_filter_t; typedef struct { dc_filter_t** dcfA; unsigned dcfN; } inst_t; rc_t create( instance_t* ctx ) { rc_t rc = kOkRC; const abuf_t* srcBuf = nullptr; // inst_t* inst = mem::allocZ(); ctx->userPtr = inst; // verify that a source buffer exists if((rc = var_register_and_get(ctx, kAnyChIdx,kInPId,"in",srcBuf )) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",ctx->label); goto errLabel; } else { // allocate channel array inst->dcfN = srcBuf->chN; inst->dcfA = mem::allocZ( inst->dcfN ); // create a dc_filter object for each input channel for(unsigned i=0; ichN; ++i) { real_t gain; bool bypassFl; // get the dc_filter variable values if((rc = var_register_and_get( ctx, i, kBypassPId, "bypass", bypassFl, kGainPId, "gain", gain )) != kOkRC ) { goto errLabel; } // create the dc_filter instance if((rc = dsp::dc_filter::create( inst->dcfA[i], srcBuf->srate, srcBuf->frameN, gain, bypassFl)) != kOkRC ) { rc = cwLogError(kOpFailRC,"The 'dc_filter' object create failed on the instance '%s'.",ctx->label); goto errLabel; } } // create the output audio buffer if((rc = var_register_and_set( ctx, "out", kOutPId, kAnyChIdx, srcBuf->srate, srcBuf->chN, srcBuf->frameN )) != kOkRC ) goto errLabel; } errLabel: return rc; } rc_t destroy( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; idcfN; ++i) destroy(inst->dcfA[i]); mem::release(inst->dcfA); mem::release(inst); return rc; } rc_t value( instance_t* ctx, variable_t* var ) { return kOkRC; } rc_t exec( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; const abuf_t* srcBuf = nullptr; abuf_t* dstBuf = nullptr; unsigned chN = 0; // get the src buffer if((rc = var_get(ctx,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) goto errLabel; // get the dst buffer if((rc = var_get(ctx,kOutPId, kAnyChIdx, dstBuf)) != kOkRC ) goto errLabel; chN = std::min(srcBuf->chN,inst->dcfN); for(unsigned i=0; i( ctx, kGainPId, i ); bool bypassFl = val_get( ctx, kBypassPId, i ); dsp::dc_filter::set( inst->dcfA[i], gain, bypassFl ); dsp::dc_filter::exec( inst->dcfA[i], srcBuf->buf + i*srcBuf->frameN, dstBuf->buf + i*srcBuf->frameN, srcBuf->frameN ); } errLabel: return rc; } rc_t report( instance_t* ctx ) { rc_t rc = kOkRC; inst_t* inst = (inst_t*)ctx->userPtr; for(unsigned i=0; idcfN; ++i) { dc_filter_t* c = inst->dcfA[i]; cwLogInfo("%s ch:%i : bypass:%i gain:%f", ctx->label,i,c->bypassFl,c->gain ); } return rc; } class_members_t members = { .create = create, .destroy = destroy, .value = value, .exec = exec, .report = report }; } } }