diff --git a/plugins/Nekobi/DistrhoPluginNekobi.cpp b/plugins/Nekobi/DistrhoPluginNekobi.cpp index d423739..90a743c 100644 --- a/plugins/Nekobi/DistrhoPluginNekobi.cpp +++ b/plugins/Nekobi/DistrhoPluginNekobi.cpp @@ -95,7 +95,7 @@ DistrhoPluginNekobi::DistrhoPluginNekobi() // Default values fParams.waveform = 0.0f; fParams.tuning = 0.0f; - fParams.cutoff = 25.0f; + fParams.cutoff = 63.0f; fParams.resonance = 25.0f; fParams.envMod = 50.0f; fParams.decay = 75.0f; @@ -105,10 +105,10 @@ DistrhoPluginNekobi::DistrhoPluginNekobi() // Internal stuff fSynth.waveform = 0.0f; fSynth.tuning = 1.0f; - fSynth.cutoff = 5.0f; + fSynth.cutoff = 63.0f; fSynth.resonance = 0.8f; - fSynth.envmod = 0.3f; - fSynth.decay = 0.0002f; + fSynth.envmod = 50.0f; + fSynth.decay = 75.0f; fSynth.accent = 0.3f; fSynth.volume = 0.75f; @@ -180,7 +180,7 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter) parameter.unit = "%"; parameter.ranges.def = 25.0f; parameter.ranges.min = 0.0f; - parameter.ranges.max = 95.0f; + parameter.ranges.max = 100.0f; parameter.midiCC = 71; break; case paramEnvMod: @@ -190,7 +190,7 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter) parameter.unit = "%"; parameter.ranges.def = 50.0f; parameter.ranges.min = 0.0f; - parameter.ranges.max = 100.0f; + parameter.ranges.max = 127.0f; parameter.midiCC = 1; //Mod Wheel break; case paramDecay: @@ -200,7 +200,7 @@ void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter) parameter.unit = "%"; parameter.ranges.def = 75.0f; parameter.ranges.min = 0.0f; - parameter.ranges.max = 100.0f; + parameter.ranges.max = 127.0f; parameter.midiCC = 72; break; case paramAccent: @@ -280,13 +280,13 @@ void DistrhoPluginNekobi::setParameterValue(uint32_t index, float value) break; case paramEnvMod: fParams.envMod = value; - fSynth.envmod = value/100.0f; - DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 1.0f); + fSynth.envmod = value; + DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 127.0f); break; case paramDecay: fParams.decay = value; - fSynth.decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log? - DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.000009f && fSynth.decay <= 0.0005f); + fSynth.decay = value; + DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.0f && fSynth.decay <= 127.0f); break; case paramAccent: fParams.accent = value; diff --git a/plugins/Nekobi/DistrhoUINekobi.cpp b/plugins/Nekobi/DistrhoUINekobi.cpp index e48c6d7..6c0c5f6 100644 --- a/plugins/Nekobi/DistrhoUINekobi.cpp +++ b/plugins/Nekobi/DistrhoUINekobi.cpp @@ -83,7 +83,7 @@ DistrhoUINekobi::DistrhoUINekobi() fKnobEnvMod = new ImageKnob(this, knobImage, ImageKnob::Vertical); fKnobEnvMod->setId(DistrhoPluginNekobi::paramEnvMod); fKnobEnvMod->setAbsolutePos(329, 43); - fKnobEnvMod->setRange(0.0f, 100.0f); + fKnobEnvMod->setRange(0.0f, 127.0f); fKnobEnvMod->setDefault(50.0f); fKnobEnvMod->setValue(50.0f); fKnobEnvMod->setRotationAngle(305); @@ -93,7 +93,7 @@ DistrhoUINekobi::DistrhoUINekobi() fKnobDecay = new ImageKnob(this, knobImage, ImageKnob::Vertical); fKnobDecay->setId(DistrhoPluginNekobi::paramDecay); fKnobDecay->setAbsolutePos(400, 43); - fKnobDecay->setRange(0.0f, 100.0f); + fKnobDecay->setRange(0.0f, 127.0f); fKnobDecay->setDefault(75.0f); fKnobDecay->setValue(75.0f); fKnobDecay->setRotationAngle(305); diff --git a/plugins/Nekobi/nekobee-src/nekobee_synth.c b/plugins/Nekobi/nekobee-src/nekobee_synth.c index 52aef62..eb65ef5 100644 --- a/plugins/Nekobi/nekobee-src/nekobee_synth.c +++ b/plugins/Nekobi/nekobee-src/nekobee_synth.c @@ -189,6 +189,8 @@ void nekobee_synth_render_voices(nekobee_synth_t *synth, float *out, wow = res * res; wow = wow / 10.0f; + + // FIXME just... fix this // as the resonance is increased, "wow" slows down the accent attack if ((synth->voice->velocity > 90) && (synth->vcf_accent < synth->voice->vcf_eg)) { @@ -205,10 +207,9 @@ void nekobee_synth_render_voices(nekobee_synth_t *synth, float *out, synth->vca_accent = 0.95 * synth->vca_accent; // accent off with time constant } -#if defined(XSYNTH_DEBUG) && (XSYNTH_DEBUG & XDB_AUDIO) - out[0] += 0.10f; /* add a 'buzz' to output so there's something audible even - when quiescent */ -#endif /* defined(XSYNTH_DEBUG) && (XSYNTH_DEBUG & XDB_AUDIO) */ + + //out[0] += 0.10f; /* add a 'buzz' to output so there's something audible even + if (_PLAYING(synth->voice)) { nekobee_voice_render(synth, out, sample_count); } diff --git a/plugins/Nekobi/nekobee-src/nekobee_voice.c b/plugins/Nekobi/nekobee-src/nekobee_voice.c index 7b3af6f..e0ec1f0 100644 --- a/plugins/Nekobi/nekobee-src/nekobee_voice.c +++ b/plugins/Nekobi/nekobee-src/nekobee_voice.c @@ -58,6 +58,7 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice, int i; voice->key = key; voice->velocity = velocity; + voice->vcf_eg = 9.8f; if (!synth->monophonic || !(_ON(voice) || _SUSTAINED(voice))) { @@ -74,14 +75,14 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice, } if (!_PLAYING(voice)) { + //printf("resetting voice\n"); voice->lfo_pos = 0.0f; voice->vca_eg = 0.0f; - voice->vcf_eg = 0.0f; + voice->vcf_eg = 9.8f; voice->delay1 = 0.0f; voice->delay2 = 0.0f; voice->delay3 = 0.0f; voice->delay4 = 0.0f; - voice->c5 = 0.0f; voice->osc_index = 0; voice->osc.phase = 0.0f; @@ -127,9 +128,10 @@ nekobee_voice_note_on(nekobee_synth_t *synth, nekobee_voice_t *voice, } synth->held_keys[0] = key; + // FIXME check if this mess is necessary if (!_PLAYING(voice)) { - - nekobee_voice_start_voice(voice); + voice->status = XSYNTH_VOICE_ON; + //nekobee_voice_start_voice(voice); } else if (!_ON(voice)) { /* must be XSYNTH_VOICE_SUSTAINED or XSYNTH_VOICE_RELEASED */ diff --git a/plugins/Nekobi/nekobee-src/nekobee_voice.h b/plugins/Nekobi/nekobee-src/nekobee_voice.h index 402a937..f45abc6 100644 --- a/plugins/Nekobi/nekobee-src/nekobee_voice.h +++ b/plugins/Nekobi/nekobee-src/nekobee_voice.h @@ -76,12 +76,13 @@ struct _nekobee_voice_t { /* persistent voice state */ float prev_pitch, target_pitch, lfo_pos; struct blosc_t osc; - float vca_eg, vcf_eg, accent_slug, delay1, delay2, delay3, delay4, c5; + float vca_eg, vcf_eg, accent_slug, delay1, delay2, delay3, delay4; + float vca_tc, vcf_tc; // VCA and VCF time constants unsigned char vca_eg_phase, vcf_eg_phase; int osc_index; /* shared index into osc_audio */ float osc_audio[XSYNTH_NUGGET_SIZE]; - float freqcut_buf[XSYNTH_NUGGET_SIZE]; - float vca_buf[XSYNTH_NUGGET_SIZE]; + float delayhp; + }; #define _PLAYING(voice) ((voice)->status != XSYNTH_VOICE_OFF) @@ -124,12 +125,4 @@ static inline void nekobee_voice_off(nekobee_voice_t *voice) { /* -FIX- decrement active voice count? */ } -/* - * nekobee_voice_start_voice - */ -static inline void nekobee_voice_start_voice(nekobee_voice_t *voice) { - voice->status = XSYNTH_VOICE_ON; - /* -FIX- increment active voice count? */ -} - #endif /* _XSYNTH_VOICE_H */ diff --git a/plugins/Nekobi/nekobee-src/nekobee_voice_render.c b/plugins/Nekobi/nekobee-src/nekobee_voice_render.c index f97e481..f5fef08 100644 --- a/plugins/Nekobi/nekobee-src/nekobee_voice_render.c +++ b/plugins/Nekobi/nekobee-src/nekobee_voice_render.c @@ -118,6 +118,9 @@ void vcf(nekobee_synth_t *synth, float *out, uint32_t count) { float delay1 = voice->delay1, delay2 = voice->delay2, delay3 = voice->delay3, delay4 = voice->delay4; + float vcf_eg = voice->vcf_eg; + float delayhp = voice->delayhp; + // to get the correct cutoff first we need Q10's collector current // The top of VR3 Cutoff is fed from 12V, the bottom from the emitter // of Q9 at around 3.2V through a 10k resistor. So, 8.8V between "rails" @@ -131,7 +134,20 @@ void vcf(nekobee_synth_t *synth, float *out, uint32_t count) { float Vcutoff = 1.47 + 7.41 * logpot[(int)floor(synth->cutoff)]; - float Vbe1 = -5.077; // mV + // similarly the envelope modulation pot is 50k log in series with 10k + // but the top of the pot is fed with the envelope voltage, about 10V at + // peak + + float Renvmod = .167 + .833 * logpot[(int)floor(synth->envmod)]; + + // subtract the 3.2V offset from Q9 + float Venvmod = (voice->vcf_eg - 3.2) * Renvmod; + + // R63 and R71 form a voltage divider, 2.2k / (220k + 2.2k) = 0.0099 + // multiply by 1000 to get a voltage in mV + + Venvmod *= 9.901; + float Vbe1 = Venvmod; // .3 is 300k expressed as MOhm // if we expressed it in Ohms output would be in A @@ -148,29 +164,48 @@ void vcf(nekobee_synth_t *synth, float *out, uint32_t count) { ct = ct / (1 + ct); // printf("cutoff = %04fHz, ct=%f\n", cutoff, ct); + float hpc = 6.28 * 16 * synth->deltat; + float fout, hp; + for (uint32_t i = 0; i < count; i++) { for (uint32_t ovs = 0; ovs < 4; ovs++) { float in = voice->osc_audio[i]; - float fb = in - (delay4 * synth->resonance * 4); - if (fb > 3) fb = 3; - if (fb < -3) fb = -3; + float fb = tanh(in - ((fout - 0.25*in) * synth->resonance * 6)); + delay1 = ((fb - delay1) * ct) + delay1; delay2 = ((delay1 - delay2) * ct) + delay2; delay3 = ((delay2 - delay3) * ct) + delay3; delay4 = ((delay3 - delay4) * ct) + delay4; + hp = ((delay4 - delayhp) * hpc ) + delayhp; + delayhp = hp; + fout = delay4-hp; } - out[i] = delay4; + out[i] = fout; + vcf_eg *= 1 - voice->vcf_tc; } - voice->delay1 = delay1; voice->delay2 = delay2; voice->delay3 = delay3; voice->delay4 = delay4; + + voice->vcf_eg = vcf_eg; + voice->delayhp = delayhp; } void nekobee_voice_render(nekobee_synth_t *synth, float *out, uint32_t count) { // generate "count" samples into the buffer at out + // FIXME factor this out into the control code + // Decay is about 2.5 seconds with the pot all the way down, and 200ms with + // it all the way up this is set by a 1M log pot in series with a 68k + // resistor, a 1uF capacitor, and a bunch of other stuff to give a correct + // DC offset and a log tailoff right at the very bottom + + synth->voice->vcf_tc = + (1 / ((68 + 1000 * logpot[(int)synth->decay]) * 0.001)) * synth->deltat; + // printf("tc = %f deltat=%f pot=%f\n",synth->voice->vcf_tc, + // synth->deltat,logpot[(int)synth->decay]); + vco(synth, count); vcf(synth, out, count);