plugin cutoff range

This commit is contained in:
Gordon JC Pearce 2023-06-20 14:40:11 +01:00
parent f0bc568030
commit bb5e064e1a
3 changed files with 406 additions and 393 deletions

View File

@ -1,387 +1,387 @@
/* /*
* DISTRHO Nekobi Plugin, based on Nekobee by Sean Bolton and others. * DISTRHO Nekobi Plugin, based on Nekobee by Sean Bolton and others.
* Copyright (C) 2004 Sean Bolton and others * Copyright (C) 2004 Sean Bolton and others
* Copyright (C) 2013-2022 Filipe Coelho <falktx@falktx.com> * Copyright (C) 2013-2022 Filipe Coelho <falktx@falktx.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as * modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of * published by the Free Software Foundation; either version 2 of
* the License, or any later version. * the License, or any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* For a full copy of the GNU General Public License see the LICENSE file. * For a full copy of the GNU General Public License see the LICENSE file.
*/ */
#include "DistrhoPluginNekobi.hpp" #include "DistrhoPluginNekobi.hpp"
#include "nekobee-src/nekobee_synth.c" #include "nekobee-src/nekobee_synth.c"
#include "nekobee-src/nekobee_voice.c" #include "nekobee-src/nekobee_voice.c"
#include "nekobee-src/nekobee_voice_render.c" #include "nekobee-src/nekobee_voice_render.c"
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// nekobee_handle_raw_event // nekobee_handle_raw_event
void nekobee_handle_raw_event(nekobee_synth_t* const synth, const uint8_t size, const uint8_t* const data) void nekobee_handle_raw_event(nekobee_synth_t* const synth, const uint8_t size, const uint8_t* const data)
{ {
if (size != 3) if (size != 3)
return; return;
switch (data[0] & 0xf0) switch (data[0] & 0xf0)
{ {
case 0x80: case 0x80:
nekobee_synth_note_off(synth, data[1], data[2]); nekobee_synth_note_off(synth, data[1], data[2]);
break; break;
case 0x90: case 0x90:
if (data[2] > 0) if (data[2] > 0)
nekobee_synth_note_on(synth, data[1], data[2]); nekobee_synth_note_on(synth, data[1], data[2]);
else else
nekobee_synth_note_off(synth, data[1], 64); /* shouldn't happen, but... */ nekobee_synth_note_off(synth, data[1], 64); /* shouldn't happen, but... */
break; break;
case 0xB0: case 0xB0:
nekobee_synth_control_change(synth, data[1], data[2]); nekobee_synth_control_change(synth, data[1], data[2]);
break; break;
default: default:
break; break;
} }
} }
START_NAMESPACE_DISTRHO START_NAMESPACE_DISTRHO
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
DistrhoPluginNekobi::DistrhoPluginNekobi() DistrhoPluginNekobi::DistrhoPluginNekobi()
: Plugin(paramCount, 0, 0) // 0 programs, 0 states : Plugin(paramCount, 0, 0) // 0 programs, 0 states
{ {
nekobee_init_tables(); nekobee_init_tables();
// init synth // init synth
fSynth.sample_rate = getSampleRate(); fSynth.sample_rate = getSampleRate();
fSynth.deltat = 1.0f / (float)getSampleRate(); fSynth.deltat = 1.0f / (float)getSampleRate();
fSynth.nugget_remains = 0; fSynth.nugget_remains = 0;
fSynth.note_id = 0; fSynth.note_id = 0;
fSynth.monophonic = XSYNTH_MONO_MODE_ONCE; fSynth.monophonic = XSYNTH_MONO_MODE_ONCE;
fSynth.glide = 0; fSynth.glide = 0;
fSynth.last_noteon_pitch = 0.0f; fSynth.last_noteon_pitch = 0.0f;
fSynth.vcf_accent = 0.0f; fSynth.vcf_accent = 0.0f;
fSynth.vca_accent = 0.0f; fSynth.vca_accent = 0.0f;
for (int i=0; i<8; ++i) for (int i=0; i<8; ++i)
fSynth.held_keys[i] = -1; fSynth.held_keys[i] = -1;
fSynth.voice = nekobee_voice_new(); fSynth.voice = nekobee_voice_new();
fSynth.channel_pressure = 0; fSynth.channel_pressure = 0;
fSynth.pitch_wheel_sensitivity = 0; fSynth.pitch_wheel_sensitivity = 0;
fSynth.pitch_wheel = 0; fSynth.pitch_wheel = 0;
for (int i=0; i<128; ++i) for (int i=0; i<128; ++i)
{ {
fSynth.key_pressure[i] = 0; fSynth.key_pressure[i] = 0;
fSynth.cc[i] = 0; fSynth.cc[i] = 0;
} }
fSynth.cc[7] = 127; // full volume fSynth.cc[7] = 127; // full volume
fSynth.mod_wheel = 1.0f; fSynth.mod_wheel = 1.0f;
fSynth.pitch_bend = 1.0f; fSynth.pitch_bend = 1.0f;
fSynth.cc_volume = 1.0f; fSynth.cc_volume = 1.0f;
// Default values // Default values
fParams.waveform = 0.0f; fParams.waveform = 0.0f;
fParams.tuning = 0.0f; fParams.tuning = 0.0f;
fParams.cutoff = 25.0f; fParams.cutoff = 25.0f;
fParams.resonance = 25.0f; fParams.resonance = 25.0f;
fParams.envMod = 50.0f; fParams.envMod = 50.0f;
fParams.decay = 75.0f; fParams.decay = 75.0f;
fParams.accent = 25.0f; fParams.accent = 25.0f;
fParams.volume = 75.0f; fParams.volume = 75.0f;
// Internal stuff // Internal stuff
fSynth.waveform = 0.0f; fSynth.waveform = 0.0f;
fSynth.tuning = 1.0f; fSynth.tuning = 1.0f;
fSynth.cutoff = 5.0f; fSynth.cutoff = 5.0f;
fSynth.resonance = 0.8f; fSynth.resonance = 0.8f;
fSynth.envmod = 0.3f; fSynth.envmod = 0.3f;
fSynth.decay = 0.0002f; fSynth.decay = 0.0002f;
fSynth.accent = 0.3f; fSynth.accent = 0.3f;
fSynth.volume = 0.75f; fSynth.volume = 0.75f;
// reset // reset
deactivate(); deactivate();
} }
DistrhoPluginNekobi::~DistrhoPluginNekobi() DistrhoPluginNekobi::~DistrhoPluginNekobi()
{ {
std::free(fSynth.voice); std::free(fSynth.voice);
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Init // Init
void DistrhoPluginNekobi::initAudioPort(bool input, uint32_t index, AudioPort& port) void DistrhoPluginNekobi::initAudioPort(bool input, uint32_t index, AudioPort& port)
{ {
port.groupId = kPortGroupMono; port.groupId = kPortGroupMono;
Plugin::initAudioPort(input, index, port); Plugin::initAudioPort(input, index, port);
} }
void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter) void DistrhoPluginNekobi::initParameter(uint32_t index, Parameter& parameter)
{ {
switch (index) switch (index)
{ {
case paramWaveform: case paramWaveform:
parameter.hints = kParameterIsAutomatable|kParameterIsInteger; parameter.hints = kParameterIsAutomatable|kParameterIsInteger;
parameter.name = "Waveform"; parameter.name = "Waveform";
parameter.symbol = "waveform"; parameter.symbol = "waveform";
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 1.0f; parameter.ranges.max = 1.0f;
parameter.enumValues.count = 2; parameter.enumValues.count = 2;
parameter.enumValues.restrictedMode = true; parameter.enumValues.restrictedMode = true;
parameter.midiCC = 70; //Sound Variation parameter.midiCC = 70; //Sound Variation
{ {
ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2]; ParameterEnumerationValue* const enumValues = new ParameterEnumerationValue[2];
enumValues[0].value = 0.0f; enumValues[0].value = 0.0f;
enumValues[0].label = "Square"; enumValues[0].label = "Square";
enumValues[1].value = 1.0f; enumValues[1].value = 1.0f;
enumValues[1].label = "Triangle"; enumValues[1].label = "Triangle";
parameter.enumValues.values = enumValues; parameter.enumValues.values = enumValues;
} }
break; break;
case paramTuning: case paramTuning:
parameter.hints = kParameterIsAutomatable; // was 0.5 <-> 2.0, log parameter.hints = kParameterIsAutomatable; // was 0.5 <-> 2.0, log
parameter.name = "Tuning"; parameter.name = "Tuning";
parameter.symbol = "tuning"; parameter.symbol = "tuning";
parameter.ranges.def = 0.0f; parameter.ranges.def = 0.0f;
parameter.ranges.min = -12.0f; parameter.ranges.min = -12.0f;
parameter.ranges.max = 12.0f; parameter.ranges.max = 12.0f;
parameter.midiCC = 75; parameter.midiCC = 75;
break; break;
case paramCutoff: case paramCutoff:
parameter.hints = kParameterIsAutomatable; // modified x2.5 parameter.hints = kParameterIsAutomatable;
parameter.name = "Cutoff"; parameter.name = "Cutoff";
parameter.symbol = "cutoff"; parameter.symbol = "cutoff";
parameter.unit = "%"; parameter.unit = "%";
parameter.ranges.def = 25.0f; parameter.ranges.def = 63.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f; parameter.ranges.max = 127.0f;
parameter.midiCC = 74; parameter.midiCC = 74;
break; break;
case paramResonance: case paramResonance:
parameter.hints = kParameterIsAutomatable; // modified x100 parameter.hints = kParameterIsAutomatable; // modified x100
parameter.name = "VCF Resonance"; parameter.name = "VCF Resonance";
parameter.symbol = "resonance"; parameter.symbol = "resonance";
parameter.unit = "%"; parameter.unit = "%";
parameter.ranges.def = 25.0f; parameter.ranges.def = 25.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 95.0f; parameter.ranges.max = 95.0f;
parameter.midiCC = 71; parameter.midiCC = 71;
break; break;
case paramEnvMod: case paramEnvMod:
parameter.hints = kParameterIsAutomatable; // modified x100 parameter.hints = kParameterIsAutomatable; // modified x100
parameter.name = "Env Mod"; parameter.name = "Env Mod";
parameter.symbol = "env_mod"; parameter.symbol = "env_mod";
parameter.unit = "%"; parameter.unit = "%";
parameter.ranges.def = 50.0f; parameter.ranges.def = 50.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f; parameter.ranges.max = 100.0f;
parameter.midiCC = 1; //Mod Wheel parameter.midiCC = 1; //Mod Wheel
break; break;
case paramDecay: case paramDecay:
parameter.hints = kParameterIsAutomatable; // was 0.000009 <-> 0.0005, log parameter.hints = kParameterIsAutomatable; // was 0.000009 <-> 0.0005, log
parameter.name = "Decay"; parameter.name = "Decay";
parameter.symbol = "decay"; parameter.symbol = "decay";
parameter.unit = "%"; parameter.unit = "%";
parameter.ranges.def = 75.0f; parameter.ranges.def = 75.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f; parameter.ranges.max = 100.0f;
parameter.midiCC = 72; parameter.midiCC = 72;
break; break;
case paramAccent: case paramAccent:
parameter.hints = kParameterIsAutomatable; // modified x100 parameter.hints = kParameterIsAutomatable; // modified x100
parameter.name = "Accent"; parameter.name = "Accent";
parameter.symbol = "accent"; parameter.symbol = "accent";
parameter.unit = "%"; parameter.unit = "%";
parameter.ranges.def = 25.0f; parameter.ranges.def = 25.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f; parameter.ranges.max = 100.0f;
parameter.midiCC = 76; parameter.midiCC = 76;
break; break;
case paramVolume: case paramVolume:
parameter.hints = kParameterIsAutomatable; // modified x100 parameter.hints = kParameterIsAutomatable; // modified x100
parameter.name = "Volume"; parameter.name = "Volume";
parameter.symbol = "volume"; parameter.symbol = "volume";
parameter.unit = "%"; parameter.unit = "%";
parameter.ranges.def = 75.0f; parameter.ranges.def = 75.0f;
parameter.ranges.min = 0.0f; parameter.ranges.min = 0.0f;
parameter.ranges.max = 100.0f; parameter.ranges.max = 100.0f;
parameter.midiCC = 7; //Volume parameter.midiCC = 7; //Volume
break; break;
} }
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Internal data // Internal data
float DistrhoPluginNekobi::getParameterValue(uint32_t index) const float DistrhoPluginNekobi::getParameterValue(uint32_t index) const
{ {
switch (index) switch (index)
{ {
case paramWaveform: case paramWaveform:
return fParams.waveform; return fParams.waveform;
case paramTuning: case paramTuning:
return fParams.tuning; return fParams.tuning;
case paramCutoff: case paramCutoff:
return fParams.cutoff; return fParams.cutoff;
case paramResonance: case paramResonance:
return fParams.resonance; return fParams.resonance;
case paramEnvMod: case paramEnvMod:
return fParams.envMod; return fParams.envMod;
case paramDecay: case paramDecay:
return fParams.decay; return fParams.decay;
case paramAccent: case paramAccent:
return fParams.accent; return fParams.accent;
case paramVolume: case paramVolume:
return fParams.volume; return fParams.volume;
} }
return 0.0f; return 0.0f;
} }
void DistrhoPluginNekobi::setParameterValue(uint32_t index, float value) void DistrhoPluginNekobi::setParameterValue(uint32_t index, float value)
{ {
switch (index) switch (index)
{ {
case paramWaveform: case paramWaveform:
fParams.waveform = value; fParams.waveform = value;
fSynth.waveform = value; fSynth.waveform = value;
DISTRHO_SAFE_ASSERT(fSynth.waveform == 0.0f || fSynth.waveform == 1.0f); DISTRHO_SAFE_ASSERT(fSynth.waveform == 0.0f || fSynth.waveform == 1.0f);
break; break;
case paramTuning: case paramTuning:
fParams.tuning = value; fParams.tuning = value;
fSynth.tuning = exp2f( value / 12.0f ); fSynth.tuning = exp2f( value / 12.0f );
DISTRHO_SAFE_ASSERT(fSynth.tuning >= 0.5f && fSynth.tuning <= 2.0f); DISTRHO_SAFE_ASSERT(fSynth.tuning >= 0.5f && fSynth.tuning <= 2.0f);
break; break;
case paramCutoff: case paramCutoff:
fParams.cutoff = value; fParams.cutoff = value;
fSynth.cutoff = value/2.5f; fSynth.cutoff = value;
DISTRHO_SAFE_ASSERT(fSynth.cutoff >= 0.0f && fSynth.cutoff <= 40.0f); DISTRHO_SAFE_ASSERT(fSynth.cutoff >= 0.0f && fSynth.cutoff <= 127.0f);
break; break;
case paramResonance: case paramResonance:
fParams.resonance = value; fParams.resonance = value;
fSynth.resonance = value/100.0f; fSynth.resonance = value/100.0f;
DISTRHO_SAFE_ASSERT(fSynth.resonance >= 0.0f && fSynth.resonance <= 0.95f); DISTRHO_SAFE_ASSERT(fSynth.resonance >= 0.0f && fSynth.resonance <= 0.95f);
break; break;
case paramEnvMod: case paramEnvMod:
fParams.envMod = value; fParams.envMod = value;
fSynth.envmod = value/100.0f; fSynth.envmod = value/100.0f;
DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 1.0f); DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 1.0f);
break; break;
case paramDecay: case paramDecay:
fParams.decay = value; fParams.decay = value;
fSynth.decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log? fSynth.decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log?
DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.000009f && fSynth.decay <= 0.0005f); DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.000009f && fSynth.decay <= 0.0005f);
break; break;
case paramAccent: case paramAccent:
fParams.accent = value; fParams.accent = value;
fSynth.accent = value/100.0f; fSynth.accent = value/100.0f;
DISTRHO_SAFE_ASSERT(fSynth.accent >= 0.0f && fSynth.accent <= 1.0f); DISTRHO_SAFE_ASSERT(fSynth.accent >= 0.0f && fSynth.accent <= 1.0f);
break; break;
case paramVolume: case paramVolume:
fParams.volume = value; fParams.volume = value;
fSynth.volume = value/100.0f; fSynth.volume = value/100.0f;
DISTRHO_SAFE_ASSERT(fSynth.volume >= 0.0f && fSynth.volume <= 1.0f); DISTRHO_SAFE_ASSERT(fSynth.volume >= 0.0f && fSynth.volume <= 1.0f);
break; break;
} }
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// Process // Process
void DistrhoPluginNekobi::activate() void DistrhoPluginNekobi::activate()
{ {
fSynth.nugget_remains = 0; fSynth.nugget_remains = 0;
fSynth.note_id = 0; fSynth.note_id = 0;
if (fSynth.voice != nullptr) if (fSynth.voice != nullptr)
nekobee_synth_all_voices_off(&fSynth); nekobee_synth_all_voices_off(&fSynth);
} }
void DistrhoPluginNekobi::deactivate() void DistrhoPluginNekobi::deactivate()
{ {
if (fSynth.voice != nullptr) if (fSynth.voice != nullptr)
nekobee_synth_all_voices_off(&fSynth); nekobee_synth_all_voices_off(&fSynth);
} }
void DistrhoPluginNekobi::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) void DistrhoPluginNekobi::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
{ {
uint32_t framesDone = 0; uint32_t framesDone = 0;
uint32_t curEventIndex = 0; uint32_t curEventIndex = 0;
uint32_t burstSize; uint32_t burstSize;
float* out = outputs[0]; float* out = outputs[0];
if (fSynth.voice == nullptr) if (fSynth.voice == nullptr)
{ {
std::memset(out, 0, sizeof(float)*frames); std::memset(out, 0, sizeof(float)*frames);
return; return;
} }
while (framesDone < frames) while (framesDone < frames)
{ {
if (fSynth.nugget_remains == 0) if (fSynth.nugget_remains == 0)
fSynth.nugget_remains = XSYNTH_NUGGET_SIZE; fSynth.nugget_remains = XSYNTH_NUGGET_SIZE;
/* process any ready events */ /* process any ready events */
for (; curEventIndex < midiEventCount && framesDone == midiEvents[curEventIndex].frame; ++curEventIndex) for (; curEventIndex < midiEventCount && framesDone == midiEvents[curEventIndex].frame; ++curEventIndex)
{ {
if (midiEvents[curEventIndex].size > MidiEvent::kDataSize) if (midiEvents[curEventIndex].size > MidiEvent::kDataSize)
continue; continue;
nekobee_handle_raw_event(&fSynth, midiEvents[curEventIndex].size, midiEvents[curEventIndex].data); nekobee_handle_raw_event(&fSynth, midiEvents[curEventIndex].size, midiEvents[curEventIndex].data);
} }
/* calculate the sample count (burstSize) for the next nekobee_voice_render() call to be the smallest of: /* calculate the sample count (burstSize) for the next nekobee_voice_render() call to be the smallest of:
* - control calculation quantization size (XSYNTH_NUGGET_SIZE, in samples) * - control calculation quantization size (XSYNTH_NUGGET_SIZE, in samples)
* - the number of samples remaining in an already-begun nugget (synth->nugget_remains) * - the number of samples remaining in an already-begun nugget (synth->nugget_remains)
* - the number of samples until the next event is ready * - the number of samples until the next event is ready
* - the number of samples left in this run * - the number of samples left in this run
*/ */
burstSize = XSYNTH_NUGGET_SIZE; burstSize = XSYNTH_NUGGET_SIZE;
/* we're still in the middle of a nugget, so reduce the burst size /* we're still in the middle of a nugget, so reduce the burst size
* to end when the nugget ends */ * to end when the nugget ends */
if (fSynth.nugget_remains < burstSize) if (fSynth.nugget_remains < burstSize)
burstSize = fSynth.nugget_remains; burstSize = fSynth.nugget_remains;
/* reduce burst size to end when next event is ready */ /* reduce burst size to end when next event is ready */
if (curEventIndex < midiEventCount && midiEvents[curEventIndex].frame - framesDone < burstSize) if (curEventIndex < midiEventCount && midiEvents[curEventIndex].frame - framesDone < burstSize)
burstSize = midiEvents[curEventIndex].frame - framesDone; burstSize = midiEvents[curEventIndex].frame - framesDone;
/* reduce burst size to end at end of this run */ /* reduce burst size to end at end of this run */
if (frames - framesDone < burstSize) if (frames - framesDone < burstSize)
burstSize = frames - framesDone; burstSize = frames - framesDone;
/* render the burst */ /* render the burst */
nekobee_synth_render_voices(&fSynth, out + framesDone, burstSize, (burstSize == fSynth.nugget_remains)); nekobee_synth_render_voices(&fSynth, out + framesDone, burstSize, (burstSize == fSynth.nugget_remains));
framesDone += burstSize; framesDone += burstSize;
fSynth.nugget_remains -= burstSize; fSynth.nugget_remains -= burstSize;
} }
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
Plugin* createPlugin() Plugin* createPlugin()
{ {
return new DistrhoPluginNekobi(); return new DistrhoPluginNekobi();
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
END_NAMESPACE_DISTRHO END_NAMESPACE_DISTRHO

View File

@ -63,9 +63,9 @@ DistrhoUINekobi::DistrhoUINekobi()
fKnobCutoff = new ImageKnob(this, knobImage, ImageKnob::Vertical); fKnobCutoff = new ImageKnob(this, knobImage, ImageKnob::Vertical);
fKnobCutoff->setId(DistrhoPluginNekobi::paramCutoff); fKnobCutoff->setId(DistrhoPluginNekobi::paramCutoff);
fKnobCutoff->setAbsolutePos(185, 43); fKnobCutoff->setAbsolutePos(185, 43);
fKnobCutoff->setRange(0.0f, 100.0f); fKnobCutoff->setRange(0.0f, 127.0f);
fKnobCutoff->setDefault(25.0f); fKnobCutoff->setDefault(50.0f);
fKnobCutoff->setValue(25.0f); fKnobCutoff->setValue(50.0f);
fKnobCutoff->setRotationAngle(305); fKnobCutoff->setRotationAngle(305);
fKnobCutoff->setCallback(this); fKnobCutoff->setCallback(this);

View File

@ -96,17 +96,30 @@ void vco(nekobee_synth_t *synth, uint32_t count) {
osc->delay = delay; osc->delay = delay;
} }
void vcf(nekobee_synth_t *synth, uint32_t count) {
// run a 4-pole ladder filter over a block
// this is a crude implementation that only approximates the complex
// behaviour of the "real" ladder filter
nekobee_voice_t *voice = synth->voice;
printf("cutoff set to %f\n", synth->cutoff);
(void)voice;
(void)count;
}
void nekobee_voice_render(nekobee_synth_t *synth, float *out, uint32_t count) { void nekobee_voice_render(nekobee_synth_t *synth, float *out, uint32_t count) {
// generate "count" samples into the buffer at out // generate "count" samples into the buffer at out
vco(synth, count); vco(synth, count);
vcf(synth, count);
for(uint32_t i=0; i<count; i++) { for(uint32_t i=0; i<count; i++) {
out[i] = synth->voice->osc_audio[i]; out[i] = synth->voice->osc_audio[i];
} }
return; return;
(void)synth;
(void)out;
(void)count;
} }