cwFlowProc.cpp: Added note_state_t for tracking the state of poly_voice_ctl and piano_voice.

Updated sustain pedal handling in piano_voice.
Updated gte_next_avail_voice() in poly_voice_ctl.
This commit is contained in:
kevin 2024-12-29 10:17:10 -05:00
parent 8ea759d11e
commit 673e93cf9e

View File

@ -4211,6 +4211,56 @@ namespace cw
}
typedef struct note_state_str
{
unsigned cycle_idx;
unsigned uid;
unsigned pitch;
unsigned vel;
unsigned status; // note-on:0x90,note-off:0x80,sound-off (0)
unsigned ch_idx;
} note_state_t;
const bool note_state_active_fl = false;
template< typename T >
void _store_note_state( proc_t* proc, T* p, unsigned uid, unsigned status, unsigned pitch, unsigned vel, unsigned ch_idx=kInvalidIdx )
{
if( note_state_active_fl && p->ns_idx < p->nsN )
{
note_state_t* ns = p->nsV + p->ns_idx;
ns->cycle_idx = proc->ctx->cycleIndex;
ns->uid = uid;
ns->pitch = pitch;
ns->vel = vel;
ns->status = status;
ns->ch_idx = ch_idx==kInvalidIdx ? proc->label_sfx_id : ch_idx;;
p->ns_idx += 1;
}
}
template< typename T >
void _write_note_state( proc_t* proc, T* p, const char* fname )
{
FILE* fp;
if( !note_state_active_fl )
return;
if((fp = fopen(fname,"w")) != NULL )
{
fprintf(fp,"ch_idx,cycle_idx,uid,status,pitch\n");
for(unsigned i=0; i<p->ns_idx; ++i)
{
const note_state_t* ns = p->nsV + i;
fprintf(fp,"%i,%i,%i,%i,%i,%i\n",ns->ch_idx,ns->cycle_idx,ns->uid,ns->status,ns->pitch,ns->vel);
}
fclose(fp);
}
}
//------------------------------------------------------------------------------------------------------------------
//
@ -4231,6 +4281,7 @@ namespace cw
typedef struct voice_str
{
bool noffFl; // true if this voice has received a note-off
bool activeFl; // true if this voice is currently active
unsigned pitch; // pitch associated with this voice
unsigned age; // age of this voice in exec() cycles.
@ -4253,6 +4304,10 @@ namespace cw
unsigned voiceMsgN;
unsigned midi_fld_idx;
note_state_t* nsV;
unsigned nsN;
unsigned ns_idx;
} inst_t;
@ -4296,11 +4351,16 @@ namespace cw
p->voiceA[i].msgA = mem::allocZ<midi::ch_msg_t>(p->voiceMsgN);
p->voiceA[i].msgN = p->voiceMsgN;
p->voiceA[i].pitch = midi::kInvalidMidiPitch;
// cache a pointer to each output variables mbuf (because we know these won't change)
if((rc = var_get(proc,kBaseOutPId+i, kAnyChIdx, p->voiceA[i].mbuf )) != kOkRC )
goto errLabel;
}
p->nsN = 500;
p->nsV = mem::allocZ<note_state_t>(p->nsN);
errLabel:
return rc;
@ -4310,6 +4370,10 @@ namespace cw
{
rc_t rc = kOkRC;
char fname[255];
snprintf(fname,255,"/home/kevin/temp/note_state/vctl.csv");
_write_note_state( proc, p, fname );
for(unsigned i=0; i<p->voiceN; ++i)
mem::release(p->voiceA[i].msgA);
@ -4330,29 +4394,41 @@ namespace cw
return rc;
}
unsigned _get_next_avail_voice( inst_t* p )
unsigned _get_next_avail_voice( inst_t* p, unsigned pitch )
{
unsigned max_age_idx = 0;
unsigned max_age_idx = 0;
unsigned inactive_idx = kInvalidIdx;
unsigned same_pitch_idx = kInvalidIdx;
for(unsigned i=0; i<p->voiceN; ++i)
{
if( p->voiceA[i].activeFl == false )
return i;
// get the inactive channel
if( inactive_idx==kInvalidIdx && p->voiceA[i].activeFl == false )
inactive_idx = i;
// check for a re-attacking note
if( p->voiceA[i].activeFl && p->voiceA[i].pitch == pitch )
same_pitch_idx = i;
if( p->voiceA[i].age > p->voiceA[ max_age_idx].age )
max_age_idx = i;
}
// BUG BUG BUG
// Uncommenting this causes output from the transforms to stop after about 30 notes
// Return the re-attacking voice index
//if( same_pitch_idx != kInvalidIdx )
// return same_pitch_idx;
if( inactive_idx != kInvalidIdx )
return inactive_idx;
cwLogWarning("Stealing:%i",p->voiceA[max_age_idx].pitch );
return max_age_idx;
}
unsigned _pitch_to_voice( inst_t* p, unsigned pitch )
{
for(unsigned i=0; i<p->voiceN; ++i)
if( p->voiceA[i].activeFl && p->voiceA[i].pitch == pitch )
return i;
return kInvalidIdx;
}
rc_t _update_voice_msg( proc_t* proc, inst_t* p, unsigned voice_idx, const midi::ch_msg_t* m )
{
@ -4380,7 +4456,7 @@ namespace cw
rc_t _on_note_on( proc_t* proc, inst_t* p, const midi::ch_msg_t* m )
{
rc_t rc = kOkRC;
unsigned voice_idx = _get_next_avail_voice(p);
unsigned voice_idx = _get_next_avail_voice(p,m->d0);
assert( voice_idx <= p->voiceN);
@ -4388,11 +4464,14 @@ namespace cw
v->age = 0;
v->activeFl = true;
v->noffFl = false;
v->pitch = m->d0;
//printf("v_idx:%i non\n",voice_idx);
rc = _update_voice_msg(proc,p,voice_idx,m);
rc = _update_voice_msg(proc,p,voice_idx,m);
_store_note_state( proc, p, m->uid, midi::kNoteOnMdId, m->d0, m->d1, voice_idx );
return rc;
}
@ -4400,16 +4479,20 @@ namespace cw
rc_t _on_note_off( proc_t* proc, inst_t* p, const midi::ch_msg_t* m )
{
rc_t rc = kOkRC;
unsigned voice_idx;
if((voice_idx = _pitch_to_voice(p,m->d0)) == kInvalidIdx )
{
cwLogWarning("Voice not found for note:%i.",m->d0);
goto errLabel;
}
for(unsigned i=0; i<p->voiceN; ++i)
if(p->voiceA[i].activeFl && p->voiceA[i].noffFl==false && p->voiceA[i].pitch==m->d0 )
{
p->voiceA[i].noffFl = true;
rc = _update_voice_msg(proc,p,i,m);
assert( voice_idx <= p->voiceN);
_store_note_state( proc, p, m->uid, midi::kNoteOffMdId, m->d0, 0, i );
rc = _update_voice_msg(proc,p,voice_idx,m);
goto errLabel;
}
cwLogWarning("Voice not found for note-off:%i.",m->d0);
errLabel:
return rc;
@ -4428,55 +4511,6 @@ namespace cw
return rc;
}
rc_t _exec0( proc_t* proc, inst_t* p )
{
rc_t rc = kOkRC;
mbuf_t* mbuf = nullptr;
// update the voice array
for(unsigned i=0; i<p->voiceN; ++i)
{
if( p->voiceA[i].activeFl )
p->voiceA[i].age += 1;
p->voiceA[i].msg_idx = 0;
p->voiceA[i].mbuf->msgN = 0;
p->voiceA[i].mbuf->msgA = nullptr;
}
// get the input MIDI buffer
if((rc = var_get(proc,kInPId,kAnyChIdx,mbuf)) != kOkRC )
goto errLabel;
// process the incoming MIDI messages
for(unsigned i=0; i<mbuf->msgN; ++i)
{
const midi::ch_msg_t* m = mbuf->msgA + i;
switch( m->status )
{
case midi::kNoteOnMdId:
if( m->d1 == 0 )
rc = _on_note_off(proc,p,m);
else
rc = _on_note_on(proc,p,m);
break;
case midi::kNoteOffMdId:
rc = _on_note_off(proc,p,m);
break;
default:
rc = _send_to_all_voices(proc,p,m);
break;
}
}
errLabel:
return rc;
}
rc_t _exec( proc_t* proc, inst_t* p )
{
@ -4491,10 +4525,15 @@ namespace cw
var_get(proc,p->baseDoneFlPId+i,kAnyChIdx,done_fl);
if( p->voiceA[i].activeFl && done_fl )
{
p->voiceA[i].activeFl = false;
p->voiceA[i].pitch = midi::kInvalidMidiPitch;
_store_note_state( proc, p, 0, 0, 0, 0, i );
}
if( p->voiceA[i].activeFl )
p->voiceA[i].age += 1;
p->voiceA[i].age += 1;
p->voiceA[i].msg_idx = 0;
p->voiceA[i].mbuf->msgN = 0;
@ -4816,6 +4855,7 @@ namespace cw
enum {
kChCnt=2
};
typedef struct
{
@ -4830,16 +4870,23 @@ namespace cw
unsigned test_pitchN; // Count of valid velocities for test_pitch
unsigned* test_pitch_map; // test_pitch_map[ test_pitch_N ]
unsigned pitch;
bool done_fl;
bool noff_fl;
bool sustain_fl;
coeff_t gain;
coeff_t gain_coeff;
coeff_t kReleaseGain;
coeff_t kGainThreshold;
bool isSustainDownFl;
bool heldByPedalFl;
note_state_t* nsV;
unsigned nsN;
unsigned ns_idx;
} inst_t;
rc_t _load_wtb(proc_t* proc, inst_t* p, const char* wtb_fname)
{
rc_t rc = kOkRC;
@ -4916,7 +4963,7 @@ namespace cw
const char* wtb_fname = nullptr;
const char* wtb_instr = nullptr;
mbuf_t* mbuf = nullptr;
bool done_fl = false;
bool done_fl = false;
srate_t srate = proc->ctx->sample_rate;
// get the MIDI input variable
@ -4965,8 +5012,12 @@ namespace cw
goto errLabel;
}
p->nsN = 100;
p->nsV = mem::allocZ<note_state_t>(p->nsN);
p->done_fl = true;
p->done_fl = true;
p->noff_fl = true;
p->sustain_fl = false;
errLabel:
@ -4993,8 +5044,6 @@ namespace cw
d0 = p->test_pitch;
}
//printf("%s:%i %i %i\n",proc->label,proc->label_sfx_id,d0,d1);
// get the wave-table associated with the pitch and velocity
if((rc = get_wave_table( *p->wtbH_ptr, p->wtb_instr_idx, d0, d1, mcs)) != kOkRC )
{
@ -5008,17 +5057,17 @@ namespace cw
rc = cwLogError(rc,"Oscilllator setup error on instr:%i pitch:%i vel:%i.",p->wtb_instr_idx,d0,d1);
goto errLabel;
}
p->pitch = d0;
p->done_fl = false;
p->noff_fl = false;
p->kGainThreshold = 0.01;
p->kReleaseGain = 0.98;
p->kReleaseGain = 0.9;
p->gain = 1.0;
p->gain_coeff = 1.0;
p->heldByPedalFl = false;
var_set(proc,kDoneFlPId,kAnyChIdx,false);
errLabel:
return rc;
}
@ -5030,22 +5079,19 @@ namespace cw
void _on_note_off( inst_t* p )
{
if( p->isSustainDownFl )
p->heldByPedalFl = true;
else
p->noff_fl = true;
if( !p->sustain_fl )
_begin_note_release(p);
//printf("%i nof: %i %i\n",proc->label_sfx_id,m->d0,m->d1);
}
void _on_sustain_pedal(proc_t* proc, inst_t* p, bool pedal_down_fl )
{
p->isSustainDownFl = pedal_down_fl;
p->sustain_fl = pedal_down_fl;
if( !p->isSustainDownFl && !p->heldByPedalFl )
if( !pedal_down_fl && p->noff_fl )
_begin_note_release(p);
//printf("%s:%i %s\n",proc->label,proc->label_sfx_id,pedal_down_fl ? "V" : "^");
_store_note_state(proc, p, 0, midi::kCtlMdId, pedal_down_fl, 0 );
}
@ -5053,6 +5099,11 @@ namespace cw
{
rc_t rc = kOkRC;
char fname[255];
snprintf(fname,255,"/home/kevin/temp/note_state/%i.csv",proc->label_sfx_id);
_write_note_state( proc, p, fname );
mem::release(p->nsV);
if( p->wtbH_ptr )
destroy(*p->wtbH_ptr);
@ -5094,13 +5145,20 @@ namespace cw
{
case midi::kNoteOnMdId:
if( m->d1 > 0 )
{
rc = _on_note_on(proc,p,m->d0,m->d1);
_store_note_state(proc, p, m->uid, midi::kNoteOnMdId, m->d0, m->d1 );
}
else
{
_on_note_off(p);
_store_note_state(proc, p, m->uid, midi::kNoteOffMdId, m->d0, 0 );
}
break;
case midi::kNoteOffMdId:
_on_note_off(p);
_store_note_state(proc, p, m->uid, midi::kNoteOnMdId, m->d0, 0 );
break;
case midi::kPbendMdId:
@ -5125,10 +5183,12 @@ namespace cw
p->gain *= p->gain_coeff;
if( (p->gain < p->kGainThreshold && !p->done_fl) || (actualFrmN < abuf->frameN) )
if( (p->gain < p->kGainThreshold && !p->done_fl) /*|| (actualFrmN < abuf->frameN)*/ )
{
p->done_fl = true;
var_set(proc,kDoneFlPId,kAnyChIdx,true);
_store_note_state(proc, p, 0, 0, p->pitch, 0);
}
errLabel:
@ -8156,7 +8216,7 @@ namespace cw
}
cwLogPrint("%i %i\n",min_idx,min_cnt);
//cwLogPrint("%i %i\n",min_idx,min_cnt);
return min_idx;
}
@ -8174,10 +8234,6 @@ namespace cw
var_set(proc,kOutChIdxPId,kAnyChIdx,p->out_idx);
}
//if((var_get(proc,kOutChIdxPId,kAnyChIdx,out_var_idx)) != kOkRC )
// goto errLabel;
// get the audio output buffers
for(unsigned i=0; i<p->outVarN; ++i)
{