cwFlowProc.cpp : Minor changes to audio_mix to clarify code.

Rewrote audio_split based on audio_mix.
This commit is contained in:
kevin 2024-06-08 17:05:14 -04:00
parent 71859d0426
commit 440c2c1944

View File

@ -83,6 +83,11 @@ namespace cw
// Custom create code goes here // Custom create code goes here
// Notes:
// 1. var_register_*() will automatically create any variables that don't already exist.
// therefore var_create() should never have to be called directly.
// 2. The variable 'vid' value must be unique across all ('label','sfx-id') pairs but not across channels.
return rc; return rc;
} }
@ -1243,32 +1248,38 @@ namespace cw
kInPId, kInPId,
kSelectPId, kSelectPId,
kInGainPId, kInGainPId,
kOutGainPId
}; };
typedef struct out_var_str
{
unsigned audioChN;
coeff_t* ogainV; // ogainV[ audioChN ]
unsigned* iChIdxV; // inChIdx[ audioChN ]
} out_var_t;
typedef struct typedef struct
{ {
unsigned oVarSelMapN; // count of input audio channels
unsigned* oVarSelMap; // [ oVarSelMapN ] selected channel map
unsigned outVarN; unsigned oVarN; // count of output variables
unsigned ogainVarN; out_var_t* oVarA; // oVarA[ oVarN ]
unsigned baseOutGainPId;
unsigned baseOutPId; unsigned baseOutPId;
unsigned iChN; // count of input audio channels
coeff_t* igainV; // igainV[ inChN ] input ch. gain coeff's
} inst_t; } inst_t;
rc_t _create( proc_t* proc, inst_t* p ) rc_t _create( proc_t* proc, inst_t* p )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
const abuf_t* abuf = nullptr; // const abuf_t* abuf = nullptr; //
const object_t* selList = nullptr; const object_t* selList = nullptr;
unsigned selListN = 0; unsigned selListN = 0;
unsigned* outVarChCntA = nullptr; unsigned* oVarSelMap = nullptr;
unsigned oGainVarN = var_mult_count(proc,"ogain");
unsigned oGainSfxIdA[ oGainVarN ];
// get the source audio buffer // get the source audio buffer
@ -1279,111 +1290,150 @@ namespace cw
goto errLabel; goto errLabel;
} }
// the input must not have 0 channels
if( abuf->chN == 0 ) if( abuf->chN == 0 )
goto errLabel; goto errLabel;
// validate the channel input->output map
if( selList == nullptr || !selList->is_list() ) if( selList == nullptr || !selList->is_list() )
{ {
rc = cwLogError(kSyntaxErrorRC,"The 'audio_split' 'select' list has invalid syntax."); rc = cwLogError(kSyntaxErrorRC,"The 'audio_split' 'select' list has invalid syntax.");
goto errLabel; goto errLabel;
} }
// there must be one in->out map for each input channel
if(( selListN = selList->child_count()) != abuf->chN ) if(( selListN = selList->child_count()) != abuf->chN )
{ {
rc = cwLogError(kInvalidArgRC,"The 'audio_split' selection list must be the same length as the count of input channels."); rc = cwLogError(kInvalidArgRC,"The 'audio_split' selection list must be the same length as the count of input channels.");
goto errLabel; goto errLabel;
} }
p->oVarSelMapN = abuf->chN; // convert in->out map to an array
p->oVarSelMap = mem::allocZ<unsigned>(p->oVarSelMapN); oVarSelMap = mem::allocZ<unsigned>(selListN);
// determine the count of output channels // determine the count of output variables
for(unsigned i = 0; i<selListN; ++i) for(unsigned i = 0; i<selListN; ++i)
{ {
unsigned oVarIdx; unsigned oVarIdx;
const object_t* listEle = selList->child_ele(i); const object_t* listEle = selList->child_ele(i);
if( listEle == nullptr || (rc = listEle->value(oVarIdx)) != kOkRC ) if( listEle == nullptr || (rc = listEle->value(oVarIdx)) != kOkRC )
{ {
rc = cwLogError(kInvalidArgRC,"The 'audio_split' selection list element at index %i is not a valid integer.",i); rc = cwLogError(kInvalidArgRC,"The 'audio_split' selection list element at index %i is not a valid integer.",i);
goto errLabel; goto errLabel;
} }
if( oVarIdx+1 > p->outVarN ) // count the number of outputs
p->outVarN = oVarIdx+1; if( oVarIdx+1 > p->oVarN )
p->oVarN = oVarIdx+1;
p->oVarSelMap[i] = oVarIdx; oVarSelMap[i] = oVarIdx;
} }
outVarChCntA = mem::allocZ<unsigned>(p->outVarN); p->oVarA = mem::allocZ<out_var_t>(p->oVarN);
p->baseOutPId = kOutGainPId + p->oVarN;
// get the count of channels for each // fill p->oVar[].audioChN with the count of channels for each output variable
for(unsigned i=0; i<selListN; ++i) for(unsigned i=0; i<selListN; ++i)
{ {
unsigned oVarIdx; unsigned oVarIdx;
selList->child_ele(i)->value(oVarIdx); selList->child_ele(i)->value(oVarIdx);
outVarChCntA[oVarIdx] += 1; p->oVarA[oVarIdx].audioChN += 1;
} }
// Get the sfx-id's of the input gain variables
if((rc = var_mult_sfx_id_array(proc, "ogain", oGainSfxIdA, oGainVarN, p->ogainVarN )) != kOkRC )
goto errLabel;
// There must be one ogain variable for each audio output or exactly one ogain variable
if( p->ogainVarN != p->outVarN && p->ogainVarN != 1 )
{
rc = cwLogError(kInvalidArgRC,"The count of 'ogain' variables must be the same as the count of out audio variables are there must be one 'ogain' variable.");
goto errLabel;
}
// set the baseOutGainPId
p->baseOutGainPId = kInGainPId + abuf->chN;
p->baseOutPId = p->baseOutGainPId + p->ogainVarN;
// register each of the output gain variables
for(unsigned i=0; i<p->ogainVarN; ++i)
if((rc = var_register(proc,kAnyChIdx,p->baseOutGainPId + i,"ogain",oGainSfxIdA[i])) != kOkRC )
goto errLabel;
// register the input gain variables
for(unsigned i=0; i<abuf->chN; ++i)
if((rc = var_register_and_set( proc, i, kInGainPId + i, "igain", kBaseSfxId, 1.0f )) != kOkRC )
goto errLabel;
// for each output variable // for each output variable
for(unsigned i=0; i<p->outVarN; ++i) for(unsigned i=0; i<p->oVarN; ++i)
{ {
variable_t* dummy = nullptr; out_var_t* ov = p->oVarA + i;
if( outVarChCntA[i] == 0 ) if( ov->audioChN == 0 )
cwLogWarning("No channels have been assigned to 'audio_split' output index %i.",i); {
cwLogWarning("No channels have been assigned to 'audio_split' output index %i on '%s:%i'.",i,cwStringNullGuard(proc->label),proc->label_sfx_id);
continue;
}
// create an output variable ov->ogainV = mem::allocZ<coeff_t>( ov->audioChN );
if( i > 0 ) ov->iChIdxV = mem::allocZ<unsigned>(ov->audioChN );
if((rc = var_create( proc, "out", i, p->baseOutPId+i, kAnyChIdx, nullptr, kInvalidTFl, dummy )) != kOkRC )
// create the output signal variable
if((rc = var_register_and_set( proc, "out", kBaseSfxId + i, p->baseOutPId+i, kAnyChIdx, abuf->srate, ov->audioChN, abuf->frameN )) != kOkRC )
goto errLabel; goto errLabel;
if((rc = var_register_and_set( proc, "out", kBaseSfxId + i, p->baseOutPId+i, kAnyChIdx, abuf->srate, outVarChCntA[i], abuf->frameN )) != kOkRC ) // register the ogain variables for this output and store the current gain values
for(unsigned j=0; j<ov->audioChN; ++j)
if((rc = var_register_and_get(proc, j, kOutGainPId + i,"ogain", kBaseSfxId + i, ov->ogainV[j])) != kOkRC )
goto errLabel; goto errLabel;
// fill ov->iChIdV[] with the input channels that are mapped to this output var
for(unsigned iChIdx=0,k=0; iChIdx<selListN; ++iChIdx)
if( oVarSelMap[iChIdx] == i )
{
assert(k<ov->audioChN);
ov->iChIdxV[k++] = iChIdx;
}
} }
p->iChN = abuf->chN;
p->igainV = mem::allocZ<coeff_t>(abuf->chN);
// register the input gain variables and store the current gain values
for(unsigned i=0; i<abuf->chN; ++i)
if((rc = var_register_and_get( proc, i, kInGainPId, "igain", kBaseSfxId, p->igainV[i] )) != kOkRC )
goto errLabel;
errLabel: errLabel:
mem::release(outVarChCntA); mem::release(oVarSelMap);
return rc; return rc;
} }
rc_t _destroy( proc_t* proc, inst_t* p ) rc_t _destroy( proc_t* proc, inst_t* p )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
mem::release(p->oVarSelMap); for(unsigned i=0; i<p->oVarN; ++i)
{
mem::release(p->oVarA[i].ogainV);
mem::release(p->oVarA[i].iChIdxV);
}
mem::release(p->oVarA);
mem::release(p->igainV);
return rc; return rc;
} }
rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) rc_t _value( proc_t* proc, inst_t* p, variable_t* var )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
switch( var->vid )
{
case kInPId:
break;
case kSelectPId:
break;
case kInGainPId:
assert( var->chIdx < p->iChN );
rc = var_get(var,p->igainV[ var->chIdx ]);
break;
default:
if( kOutGainPId <= var->vid && var->vid < kOutGainPId + p->oVarN )
{
unsigned oVarIdx = var->label_sfx_id - kBaseSfxId;
assert( oVarIdx < p->oVarN && var->chIdx < p->oVarA[oVarIdx].audioChN );
rc = var_get(var,p->oVarA[ oVarIdx ].ogainV[var->chIdx]);
}
break;
break;
}
return rc; return rc;
} }
@ -1391,48 +1441,33 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
if( p->outVarN )
{
const abuf_t* ibuf = nullptr; const abuf_t* ibuf = nullptr;
unsigned outVarChIdxA[ p->outVarN ]; // map output var to next audio ch idx
vop::zero(outVarChIdxA,p->outVarN);
// get the src buffer // get the input audio buffer
if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC ) if((rc = var_get(proc,kInPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel; goto errLabel;
// for each input channel for(unsigned i=0; i<p->oVarN; ++i)
for(unsigned iChIdx=0; iChIdx<ibuf->chN; ++iChIdx)
{ {
abuf_t* obuf = nullptr; abuf_t* obuf = nullptr;
unsigned oVarIdx = p->oVarSelMap[iChIdx];
unsigned oGainVId = p->baseOutGainPId + (p->ogainVarN==1 ? 0 : oVarIdx);
unsigned oVId = p->baseOutPId + oVarIdx;
unsigned oChIdx = outVarChIdxA[ oVarIdx ];
outVarChIdxA[oVarIdx] += 1; // set the next audio ch for this output var // get the ith output buffer
if((rc = var_get(proc, p->baseOutPId +i, kAnyChIdx, obuf)) != kOkRC )
// get the dst buffer
if((rc = var_get(proc,oVId, kAnyChIdx, obuf)) != kOkRC )
goto errLabel; goto errLabel;
else
for( unsigned oChIdx=0; oChIdx<obuf->chN; ++oChIdx)
{ {
unsigned iChIdx = p->oVarA[i].iChIdxV[oChIdx];
coeff_t ogain = p->oVarA[i].ogainV[oChIdx];
coeff_t igain = p->igainV[iChIdx];
coeff_t gain = igain * ogain;
sample_t* isig = ibuf->buf + iChIdx * ibuf->frameN; sample_t* isig = ibuf->buf + iChIdx * ibuf->frameN;
sample_t* osig = obuf->buf + oChIdx * obuf->frameN; sample_t* osig = obuf->buf + oChIdx * obuf->frameN;
coeff_t igain = 1;
coeff_t ogain = 1;
if((rc = var_get(proc, kInGainPId + iChIdx, iChIdx, igain)) != kOkRC )
goto errLabel;
if((rc = var_get(proc, oGainVId, kAnyChIdx, ogain)) != kOkRC )
goto errLabel;
// apply the gain
for(unsigned j=0; j<ibuf->frameN; ++j) for(unsigned j=0; j<ibuf->frameN; ++j)
osig[j] = igain * ogain * isig[j]; osig[j] = gain * isig[j];
}
} }
} }
errLabel: errLabel:
@ -1598,6 +1633,7 @@ namespace cw
{ {
enum { enum {
kOutPId, kOutPId,
kOutGainPId,
kInBasePId, kInBasePId,
}; };
@ -1606,7 +1642,7 @@ namespace cw
unsigned sfx_id; // sfx_id of both the audio and gain var's unsigned sfx_id; // sfx_id of both the audio and gain var's
unsigned audioChN; // count of audio channels unsigned audioChN; // count of audio channels
unsigned aVId; // (there can only be one audio var.) unsigned aVId; // (there can only be one audio var.)
unsigned gBaseVId; // (there is one gain var. per audio channel) unsigned gVId; // (there is one gain var. per audio channel)
coeff_t* gainV; // gainV[ audioChN ] coeff_t* gainV; // gainV[ audioChN ]
} audio_gain_t; } audio_gain_t;
@ -1623,41 +1659,8 @@ namespace cw
} inst_t; } inst_t;
rc_t _mix_0( proc_t* proc, unsigned inPId, unsigned igainPId, abuf_t* obuf, coeff_t* ogainV ) // Mix the the first N channels of the input audio signal from iag->aVId
{ // into the first N channels of the output signal.
rc_t rc = kOkRC;
const abuf_t* ibuf = nullptr;
// get the input audio buffer
if((rc = var_get(proc, inPId, kAnyChIdx, ibuf )) != kOkRC )
goto errLabel;
else
{
// get the min count of channels between in and out buffers
unsigned chN = std::min(ibuf->chN, obuf->chN );
for(unsigned i=0; i<chN; ++i)
{
coeff_t gain = 1;
const sample_t* isig = ibuf->buf + i*ibuf->frameN;
sample_t* osig = obuf->buf + i*obuf->frameN;
// get the input gain for this channel
if((rc = var_get(proc, igainPId, kAnyChIdx, gain)) != kOkRC )
goto errLabel;
gain *= ogainV[i];
for(unsigned j=0; j<obuf->frameN; ++j)
osig[j] += gain * isig[j];
}
}
errLabel:
return rc;
}
rc_t _mix( proc_t* proc, audio_gain_t* iag, audio_gain_t* oag, abuf_t* obuf ) rc_t _mix( proc_t* proc, audio_gain_t* iag, audio_gain_t* oag, abuf_t* obuf )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
@ -1685,54 +1688,27 @@ namespace cw
} }
// Be sure that there is a gain channel instantiated for every audio channel // Be sure that there is a gain channel instantiated for every audio channel
// and fill ag->gainV[] with the current value of each pre-created gain variable // and fill ag->gainV[] with the current value of each pre-created gain variable.
// or set it to 1. // or the class default gain value if no pre-created gain variable exists.
rc_t _setup_gain( proc_t* proc, const char* var_label, audio_gain_t* ag ) rc_t _setup_gain( proc_t* proc, const char* var_label, unsigned sfx_id, unsigned a_vid, unsigned g_vid, unsigned audioChN, audio_gain_t* ag )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
// if the base igain var does not exist .. // setup the gain control record
if( !var_exists( proc, var_label, ag->sfx_id, kAnyChIdx ) ) ag->audioChN = audioChN;
{ ag->gainV = mem::allocZ<coeff_t>(audioChN);
variable_t* var = nullptr; ag->sfx_id = sfx_id;
ag->aVId = a_vid;
ag->gVId = g_vid;
// ... then create it vop::fill(ag->gainV,audioChN,1);
if((rc = var_create(proc, var_label, ag->sfx_id, ag->gBaseVId, kAnyChIdx, nullptr, kInvalidTFl, var )) != kOkRC )
{ // register audio gain variables for each channel of the audio signal represented by ag
rc = cwLogError(rc,"'igain' var create failed."); for(unsigned i=0; i<ag->audioChN; ++i)
if((rc = var_register_and_get(proc, i, g_vid, var_label, sfx_id, ag->gainV[i])) != kOkRC )
goto errLabel; goto errLabel;
}
// ... ans set the gain to 1.0
var_set(var,1.0f);
}
// for each audio channel
for(unsigned j=0; j<ag->audioChN; ++j)
{
// if the gain var exists for this audio var/channel
if( var_exists(proc,var_label, ag->sfx_id, j ) )
{
// then register it and store the gain value
if((rc = var_register_and_get(proc, j, ag->gBaseVId+j, var_label, ag->sfx_id, ag->gainV[j])) != kOkRC )
{
goto errLabel;
}
}
else
{
// otherwise create the channel var. via channelization
variable_t* var = nullptr;
if((rc = var_channelize( proc, var_label, ag->sfx_id, j, nullptr, ag->gBaseVId+j, var )) != kOkRC )
{
goto errLabel;
}
}
}
errLabel: errLabel:
return rc; return rc;
} }
@ -1741,7 +1717,6 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
unsigned nextInGainPId = 0;
unsigned audioFrameN = 0; unsigned audioFrameN = 0;
unsigned maxInAudioChCnt = 0; unsigned maxInAudioChCnt = 0;
srate_t srate = 0; srate_t srate = 0;
@ -1755,8 +1730,9 @@ namespace cw
p->iagV = mem::allocZ<audio_gain_t>(p->inAudioVarCnt); p->iagV = mem::allocZ<audio_gain_t>(p->inAudioVarCnt);
// set the baseInGainPId // set the baseInGainPId
nextInGainPId = p->baseInGainPId = kInBasePId + p->inAudioVarCnt; p->baseInGainPId = kInBasePId + p->inAudioVarCnt;
// for each audio input var
for(unsigned i=0; i<p->inAudioVarCnt; ++i) for(unsigned i=0; i<p->inAudioVarCnt; ++i)
{ {
abuf_t* abuf; abuf_t* abuf;
@ -1783,40 +1759,22 @@ namespace cw
audioFrameN = abuf->frameN; audioFrameN = abuf->frameN;
// track the max channel count among all audio input variables
if( abuf->chN > maxInAudioChCnt ) if( abuf->chN > maxInAudioChCnt )
maxInAudioChCnt = abuf->chN; maxInAudioChCnt = abuf->chN;
// setup the audio_gain record for this audio channel // setup the audio_gain record for this input audio variable
p->iagV[i].audioChN = abuf->chN; if((rc= _setup_gain(proc, "igain", aSfxIdA[i], kInBasePId+i, p->baseInGainPId+i, abuf->chN, p->iagV + i )) != kOkRC )
p->iagV[i].gainV = mem::allocZ<coeff_t>(abuf->chN);
p->iagV[i].sfx_id = aSfxIdA[i];
p->iagV[i].aVId = kInBasePId + i;
p->iagV[i].gBaseVId = nextInGainPId;
vop::fill(p->iagV[i].gainV,abuf->chN,1);
// setup the input gain for this input audio variable
if((rc= _setup_gain(proc, "igain", p->iagV + i )) != kOkRC )
goto errLabel; goto errLabel;
nextInGainPId += abuf->chN;
} }
p->baseOutGainPId = nextInGainPId;
if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, maxInAudioChCnt, audioFrameN )) != kOkRC ) if((rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, maxInAudioChCnt, audioFrameN )) != kOkRC )
{ {
goto errLabel; goto errLabel;
} }
p->oag.audioChN = maxInAudioChCnt; // setup the audio_gain record for the output gains
p->oag.gainV = mem::allocZ<coeff_t>(maxInAudioChCnt); if((rc= _setup_gain(proc, "ogain", kBaseSfxId, kOutPId, kOutGainPId, maxInAudioChCnt, &p->oag )) != kOkRC )
p->oag.sfx_id = kBaseSfxId;
p->oag.aVId = kOutPId;
p->oag.gBaseVId = p->baseOutGainPId;
vop::fill(p->oag.gainV,p->oag.audioChN,1);
if((rc= _setup_gain(proc, "ogain", &p->oag )) != kOkRC )
goto errLabel; goto errLabel;
@ -1842,35 +1800,41 @@ namespace cw
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
switch( var->vid )
{
case kOutPId:
break;
case kOutGainPId:
assert( var->chIdx < p->oag.audioChN);
var_get(var,p->oag.gainV[var->chIdx] ); // ... update the associated gainV[] value
break;
default:
// if this is an in-gain value // if this is an in-gain value
if( p->baseInGainPId <= var->vid && var->vid < p->oag.gBaseVId ) if( p->baseInGainPId <= var->vid && var->vid < p->baseInGainPId + p->inAudioVarCnt )
{ {
// determine which in-gain variable this var is associated with // determine which in-gain variable this var is associated with
for(unsigned i=0; i<p->inAudioVarCnt; ++i) for(unsigned i=0; i<p->inAudioVarCnt; ++i)
if( p->iagV[i].gBaseVId <= var->vid && var->vid < p->iagV[i].gBaseVId + p->iagV[i].audioChN ) if( p->iagV[i].gVId == var->vid )
{ {
// ... update the associated gainV[] value // ... update the associated gainV[] value
unsigned ch_idx = var->vid - p->iagV[i].gBaseVId; assert( var->chIdx < p->iagV[i].audioChN);
var_get(var,p->iagV[i].gainV[ch_idx]); var_get(var,p->iagV[i].gainV[var->chIdx]);
goto errLabel; goto errLabel;
} }
assert(0);
} }
else else
{ {
// if this is an out-gain value ... assert(kInBasePId <= var->vid && var->vid < kInBasePId + p->inAudioVarCnt);
if( p->oag.gBaseVId <= var->vid && var->vid <= p->oag.gBaseVId + p->oag.audioChN )
{
unsigned ch_idx = var->vid - p->oag.gBaseVId;
var_get(var,p->oag.gainV[ch_idx] ); // ... update the associated gainV[] value
}
else
{
assert( var->vid==kOutPId || (kInBasePId <= var->vid && var->vid < kInBasePId + p->inAudioVarCnt));
} }
} }
errLabel: errLabel:
return rc; return rc;
} }
@ -1882,11 +1846,8 @@ namespace cw
// get the output audio buffer // get the output audio buffer
if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC ) if((rc = var_get(proc,kOutPId, kAnyChIdx, obuf)) != kOkRC )
{
goto errLabel; goto errLabel;
}
else
{
// zero the output buffer // zero the output buffer
vop::zero(obuf->buf, obuf->frameN*obuf->chN ); vop::zero(obuf->buf, obuf->frameN*obuf->chN );
@ -1894,7 +1855,7 @@ namespace cw
for(unsigned i=0; i<p->inAudioVarCnt; ++i) for(unsigned i=0; i<p->inAudioVarCnt; ++i)
if((rc =_mix(proc, p->iagV + i, &p->oag, obuf )) != kOkRC ) if((rc =_mix(proc, p->iagV + i, &p->oag, obuf )) != kOkRC )
goto errLabel; goto errLabel;
}
errLabel: errLabel:
return rc; return rc;
} }