diff --git a/cwFlow.cpp b/cwFlow.cpp index 324775b..8758b2b 100644 --- a/cwFlow.cpp +++ b/cwFlow.cpp @@ -43,6 +43,7 @@ namespace cw { "audio_duplicate", &audio_duplicate::members }, { "audio_merge", &audio_merge::members }, { "audio_mix", &audio_mix::members }, + { "audio_silence", &audio_silence::members }, { "sine_tone", &sine_tone::members }, { "pv_analysis", &pv_analysis::members }, { "pv_synthesis", &pv_synthesis::members }, diff --git a/cwFlowProc.cpp b/cwFlowProc.cpp index 674846d..564468b 100644 --- a/cwFlowProc.cpp +++ b/cwFlowProc.cpp @@ -2115,6 +2115,72 @@ namespace cw }; } + + //------------------------------------------------------------------------------------------------------------------ + // + // audio_silence + // + namespace audio_silence + { + enum { + kSratePId, + kChCntPId, + kOutPId + }; + + typedef struct + { + + } inst_t; + + + rc_t _create( proc_t* proc, inst_t* p ) + { + rc_t rc = kOkRC; + srate_t srate = 0; + unsigned ch_cnt = 1; + + + if((rc = var_register_and_get(proc, kAnyChIdx, + kSratePId,"srate",kBaseSfxId,srate, + kChCntPId,"ch_cnt",kBaseSfxId,ch_cnt)) != kOkRC ) + { + goto errLabel; + } + + if( srate == 0 ) + srate = proc->ctx->sample_rate; + + + // create the output audio buffer + rc = var_register_and_set( proc, "out", kBaseSfxId, kOutPId, kAnyChIdx, srate, ch_cnt, proc->ctx->framesPerCycle ); + + errLabel: + return rc; + } + + rc_t _destroy( proc_t* proc, inst_t* p ) + { return kOkRC; } + + rc_t _value( proc_t* proc, inst_t* p, variable_t* var ) + { return kOkRC; } + + rc_t _exec( proc_t* proc, inst_t* p ) + { return kOkRC; } + + rc_t _report( proc_t* proc, inst_t* p ) + { return kOkRC; } + + class_members_t members = { + .create = std_create, + .destroy = std_destroy, + .value = std_value, + .exec = std_exec, + .report = std_report + }; + + } + //------------------------------------------------------------------------------------------------------------------ // @@ -3493,7 +3559,8 @@ namespace cw kPeakDbPId, kOutPId, kPeakFlPId, - kClipFlPId + kClipFlPId, + kRptPeriodMsPId }; @@ -3503,25 +3570,32 @@ namespace cw { audio_meter_t** mtrA; unsigned mtrN; + unsigned rptPeriodSmpN; + unsigned rptPhase; } inst_t; rc_t create( proc_t* proc ) { - rc_t rc = kOkRC; - const abuf_t* srcBuf = nullptr; // - inst_t* inst = mem::allocZ(); + rc_t rc = kOkRC; + const abuf_t* srcBuf = nullptr; // + inst_t* inst = mem::allocZ(); + unsigned rptPeriodMs = 0; proc->userPtr = inst; // verify that a source buffer exists - if((rc = var_register_and_get(proc, kAnyChIdx,kInPId,"in",kBaseSfxId,srcBuf )) != kOkRC ) + if((rc = var_register_and_get(proc, kAnyChIdx, + kInPId,"in",kBaseSfxId,srcBuf, + kRptPeriodMsPId,"rpt_ms",kBaseSfxId,rptPeriodMs)) != kOkRC ) { rc = cwLogError(rc,"The instance '%s' does not have a valid input connection.",proc->label); goto errLabel; } else { + inst->rptPeriodSmpN = (unsigned)(proc->ctx->sample_rate * rptPeriodMs/1000.0); + // allocate channel array inst->mtrN = srcBuf->chN; inst->mtrA = mem::allocZ( inst->mtrN ); @@ -3531,7 +3605,7 @@ namespace cw { ftime_t wndMs; coeff_t peakThreshDb; - bool dbFl; + bool dbFl; // get the audio_meter variable values if((rc = var_register_and_get( proc, i, @@ -3593,6 +3667,8 @@ namespace cw inst_t* inst = (inst_t*)proc->userPtr; const abuf_t* srcBuf = nullptr; unsigned chN = 0; + + bool rptFl = inst->rptPeriodSmpN != 0 && inst->rptPhase >= inst->rptPeriodSmpN; // get the src buffer if((rc = var_get(proc,kInPId, kAnyChIdx, srcBuf )) != kOkRC ) @@ -3606,8 +3682,20 @@ namespace cw var_set(proc, kOutPId, i, inst->mtrA[i]->outDb ); var_set(proc, kPeakFlPId, i, inst->mtrA[i]->peakFl ); var_set(proc, kClipFlPId, i, inst->mtrA[i]->clipFl ); - } + if( rptFl ) + cwLogPrint("%6.2f ",inst->mtrA[i]->outDb); + } + + if(rptFl) + { + cwLogPrint("\n"); + inst->rptPhase -= inst->rptPeriodSmpN; + } + + inst->rptPhase += srcBuf->frameN; + + errLabel: return rc; } @@ -4187,7 +4275,12 @@ namespace cw double cur_pbend; // current pitch bend factor unsigned hzN; - double* hzA; // hzA[128] - midi to Hz lookup table. + double* hzA; // hzA[128] - midi to Hz lookup table. + + bool done_fl; + coeff_t gain; + coeff_t gain_coeff; + coeff_t gain_thresh; } inst_t; @@ -4227,6 +4320,8 @@ namespace cw for(unsigned i=0; ihzA[i] = midi_to_hz(i); + p->done_fl = true; + errLabel: return rc; } @@ -4247,6 +4342,11 @@ namespace cw return rc; } + void _on_note_off( inst_t* p ) + { + p->gain_coeff = 0.9; + } + rc_t _exec( proc_t* proc, inst_t* p ) { rc_t rc = kOkRC; @@ -4272,11 +4372,18 @@ namespace cw p->cur_vel = m->d1; if( m->d1 == 0 ) - var_set(proc,kDoneFlPId,kAnyChIdx,true); + _on_note_off(p); + else + { + p->done_fl = false; + p->gain = (coeff_t)p->cur_vel / 127; + p->gain_coeff = 1.0; + p->gain_thresh = 0.001; + } break; case midi::kNoteOffMdId: - var_set(proc,kDoneFlPId,kAnyChIdx,true); + _on_note_off(p); break; case midi::kPbendMdId: @@ -4289,16 +4396,13 @@ namespace cw } // if the voice is off then zero the audio buffer - if( p->cur_vel == 0 ) + if( p->done_fl ) { vop::zero(abuf->buf,abuf->frameN); } else { - // calculate the gain based on the cur_vel - coeff_t gain = (coeff_t)p->cur_vel / 127; - // fill in the audio buffer for(unsigned i=0; iframeN; ++i) { @@ -4306,12 +4410,21 @@ namespace cw double frac = p->wtPhase - j; sample_t smp = p->wtA[j] + (p->wtA[j+1] - p->wtA[j]) * frac; - abuf->buf[i] = gain*smp; + abuf->buf[i] = p->gain*smp; p->wtPhase += p->cur_hz + (p->cur_hz * p->cur_pbend); if( p->wtPhase >= p->wtN ) p->wtPhase -= p->wtN; } + + p->gain *= p->gain_coeff; + + if( p->gain < p->gain_thresh ) + { + var_set(proc,kDoneFlPId,kAnyChIdx,true); + p->done_fl = true; + } + } errLabel: @@ -4344,6 +4457,7 @@ namespace cw kOutPId, kDoneFlPId, kTestPitchPId, + kKeyPitchPId, }; enum { @@ -4374,7 +4488,8 @@ namespace cw coeff_t gain_coeff; bool done_fl; - unsigned test_pitch; // Base test pitch + unsigned test_pitch; // Pitch under test or 0 if not on test mode + unsigned test_key_pitch; // Key associated with lowest velocity when in test mode. unsigned test_pitchN; // Count of valid velocities for test_pitch unsigned* test_pitch_map; // test_pitch_map[ test_pitch_N ] @@ -4395,6 +4510,7 @@ namespace cw { assert( j < p->test_pitchN ); p->test_pitch_map[j++] = i; + //printf("%i %i %i\n",j-1,i,p->test_pitchN); } } @@ -4417,7 +4533,8 @@ namespace cw kWtbInstrPId, "wtb_instr", kBaseSfxId, wtb_instr, kInPId, "in", kBaseSfxId, mbuf, kDoneFlPId, "done_fl", kBaseSfxId, done_fl, - kTestPitchPId, "test_pitch",kBaseSfxId, p->test_pitch)) != kOkRC ) + kTestPitchPId, "test_pitch",kBaseSfxId, p->test_pitch, + kKeyPitchPId, "test_key_pitch", kBaseSfxId, p->test_key_pitch)) != kOkRC ) { goto errLabel; } @@ -4536,8 +4653,8 @@ namespace cw // if in voice test mode if( p->test_pitch_map != nullptr ) { - // if the the pitch is in side the test range - if( d0 < p->test_pitch || p->test_pitch + p->test_pitchN <= d1 ) + // if the the pitch is inside the test range + if( d0 < p->test_key_pitch || p->test_key_pitch + p->test_pitchN <= d0 ) goto errLabel; // then the pitch is set to the test pitch ... @@ -4545,7 +4662,7 @@ namespace cw // ... and the velocity is mapped to a vel for which there is a known vel in the wt-bank // Performed pitches above the test pitch trigger increasing velocities. - d1 = p->test_pitch_map[ m->d0 - p->test_pitch ]; + d1 = p->test_pitch_map[ m->d0 - p->test_key_pitch ]; } // get the wave-table associated with the pitch and velocity @@ -4598,17 +4715,23 @@ namespace cw else { + // for each channel for(unsigned i=0; ibuf + i*abuf->frameN; + // for the output buffer + sample_t* yV = abuf->buf + i*abuf->frameN; unsigned yi = 0; + + // get this channels oscillator osc_state_t* osc = p->osc + i; + // for each sample in the output buffer while(yi < abuf->frameN) { + // locate the current wavetable wt_bank::seg_t* seg = p->wt->chA[i].segA + osc->seg_idx; unsigned n; - + if( seg->aN - osc->smp_idx > abuf->frameN-yi ) n = abuf->frameN-yi; else @@ -4639,7 +4762,7 @@ namespace cw if( p->gain < p->kGainThreshold && !p->done_fl ) { - //printf("done\n"); + //printf("done:\n"); var_set(proc,kDoneFlPId,kAnyChIdx,true); p->done_fl = true; } diff --git a/cwFlowProc.h b/cwFlowProc.h index d464325..96aed14 100644 --- a/cwFlowProc.h +++ b/cwFlowProc.h @@ -16,6 +16,7 @@ namespace cw namespace audio_duplicate { extern class_members_t members; } namespace audio_mix { extern class_members_t members; } namespace audio_marker { extern class_members_t members; } + namespace audio_silence { extern class_members_t members; } namespace sine_tone { extern class_members_t members; } namespace pv_analysis { extern class_members_t members; } namespace pv_synthesis { extern class_members_t members; } diff --git a/flow/proc_dict.cfg b/flow/proc_dict.cfg index 615434f..12ab3df 100644 --- a/flow/proc_dict.cfg +++ b/flow/proc_dict.cfg @@ -130,6 +130,14 @@ } } + audio_silence: { + vars: { + srate: { type:srate, value:0, flags:["init"], doc:"Signal sample rate. 0=Use default system sample rate"}, + ch_cnt: { type:uint, value:1, flags:["init"], doc:"Count of output audio channels. (e.g. 1=mono, 2=stereo, ...)"}, + out: { type:audio, doc:"Audio signal containing only 0."}, + } + } + sine_tone: { vars: { @@ -545,7 +553,8 @@ peakDb: { type:coeff, value: -10.0, doc:"Peak threshold." }, out: { type:coeff, value: 0.0, doc:"Meter output." }, peakFl: { type:bool, value: false, doc:"Peak output." } - clipFl: { type:bool, value: false, doc:"Clip indicator output."} + clipFl: { type:bool, value: false, doc:"Clip indicator output."}, + rpt_ms: { type:uint, value:0, flags:["init"], doc:"Report period in ms or 0 for no report."}, } } @@ -715,7 +724,8 @@ in: { type:midi, doc:"MIDI in" }, out: { type:audio, doc:"Audio out" }, done_fl: { type:bool, value:false, doc:"Triggers when voice is available."}, - test_pitch: { type:uint, value:0, doc:"Base testing pitch." }, + test_pitch: { type:uint, value:0, doc:"Pitch to test." }, + test_key_pitch: { type:uint, value:48, doc:"Base pitch to use for lowest velocity when in 'test' mode." }, } },