commit 432b812c93a5b521d336e4a35799598b5924daf5
parent be617d8cf088763c5f70409b007300560798d254
Author: Matt Demanett <matt@demanett.net>
Date: Sat, 24 Mar 2018 18:27:09 -0400
FM/PM fixes all around.
Diffstat:
8 files changed, 129 insertions(+), 49 deletions(-)
diff --git a/res-src/VCO-src.svg b/res-src/VCO-src.svg
@@ -166,6 +166,44 @@
</g>
</symbol>
+ <symbol id="knobguide-fm" viewBox="0 0 45px 45px">
+ <g transform="translate(22.5 22.5)">
+ <g transform="rotate(-240) translate(15 0)">
+ <text font-size="5.0pt" transform="translate(3 0) rotate(240) translate(-2.2 2.2)">0</text>
+ </g>
+ <g transform="rotate(-210) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-180) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-150) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-120) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-90) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-60) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(-30) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(0) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(30) translate(15 0)">
+ <polyline points="0,0 2.5,0" stroke-width="0.3" stroke="#333" />
+ </g>
+ <g transform="rotate(60) translate(15 0)">
+ <text font-size="5.0pt" transform="translate(3 0) rotate(-60) translate(-2.2 2.2)">1</text>
+ </g>
+ </g>
+ </symbol>
+
<symbol id="knobguide-centertick" viewBox="0 0 40px 40px">
<g transform="translate(20 20)">
<g transform="rotate(-90) translate(10 0)">
@@ -278,7 +316,7 @@
</g>
</g>
</g>
- <use xlink:href="#knobguide-attenuverter" transform="translate(18.5 221.5)" />
+ <use xlink:href="#knobguide-fm" transform="translate(18.5 221.5)" />
<!-- <rect width="100" height="6" fill="#0f0" transform="translate(10 263)" /> -->
diff --git a/res/VCO.svg b/res/VCO.svg
Binary files differ.
diff --git a/src/FMOp.cpp b/src/FMOp.cpp
@@ -18,7 +18,7 @@ void FMOp::onSampleRateChange() {
void FMOp::step() {
lights[ENV_TO_LEVEL_LIGHT].value = _levelEnvelopeOn;
lights[ENV_TO_FEEDBACK_LIGHT].value = _feedbackEnvelopeOn;
- if (outputs[AUDIO_OUTPUT].active) {
+ if (!outputs[AUDIO_OUTPUT].active) {
return;
}
@@ -48,14 +48,18 @@ void FMOp::step() {
_baseHZ = cvToFrequency(_baseHZ);
_baseHZ *= ratio;
- bool levelEnvelopeOn = params[ENV_TO_LEVEL_PARAM].value < 0.5f;
- if (_levelEnvelopeOn != levelEnvelopeOn) {
- if (_levelEnvelopeOn) {
+ bool levelEnvelopeOn = params[ENV_TO_LEVEL_PARAM].value > 0.5f;
+ bool feedbackEnvelopeOn = params[ENV_TO_FEEDBACK_PARAM].value > 0.5f;
+ if (_levelEnvelopeOn != levelEnvelopeOn || _feedbackEnvelopeOn != feedbackEnvelopeOn) {
+ _levelEnvelopeOn = levelEnvelopeOn;
+ _feedbackEnvelopeOn = feedbackEnvelopeOn;
+ bool envelopeOn = _levelEnvelopeOn || _feedbackEnvelopeOn;
+ if (envelopeOn && !_envelopeOn) {
_envelope.reset();
}
- _levelEnvelopeOn = levelEnvelopeOn;
+ _envelopeOn = envelopeOn;
}
- if (_levelEnvelopeOn) {
+ if (_envelopeOn) {
float sustain = params[SUSTAIN_PARAM].value;
if (inputs[SUSTAIN_INPUT].active) {
sustain *= clamp(inputs[SUSTAIN_INPUT].value / 10.0f, 0.0f, 1.0f);
@@ -82,20 +86,29 @@ void FMOp::step() {
}
}
+ float envelope = 0.0f;
+ if (_envelopeOn) {
+ _gateTrigger.process(gateIn);
+ _envelope.setGate(_gateTrigger.isHigh());
+ envelope = _envelope.next();
+ }
+
+ float feedback = _feedback;
+ if (_feedbackEnvelopeOn) {
+ feedback *= envelope;
+ }
+
_phasor.setFrequency(_baseHZ);
- float offset = _feedback * _phasor.next();
+ float offset = feedback * _phasor.next();
if (inputs[FM_INPUT].active) {
- offset += clamp(inputs[FM_INPUT].value / 5.0f, -1.0f, 1.0f) * _depth * 10.0f;
+ offset += inputs[FM_INPUT].value * _depth * 2.0f;
}
float out = _sineTable.nextFromPhasor(_phasor, Phasor::radiansToPhase(offset));
out *= _level;
if (_levelEnvelopeOn) {
- _gateTrigger.process(gateIn);
- _envelope.setGate(_gateTrigger.isHigh());
- out *= _envelope.next();
+ out *= envelope;
}
-
- outputs[AUDIO_OUTPUT].value = out * 5.0f;
+ outputs[AUDIO_OUTPUT].value = amplitude * out;
}
struct FMOpWidget : ModuleWidget {
@@ -147,7 +160,7 @@ struct FMOpWidget : ModuleWidget {
addParam(ParamWidget::create<Knob26>(decayParamPosition, module, FMOp::DECAY_PARAM, 0.0, 1.0, 0.1));
addParam(ParamWidget::create<Knob26>(sustainParamPosition, module, FMOp::SUSTAIN_PARAM, 0.0, 1.0, 1.0));
addParam(ParamWidget::create<Knob26>(releaseParamPosition, module, FMOp::RELEASE_PARAM, 0.0, 1.0, 0.3));
- addParam(ParamWidget::create<Knob26>(depthParamPosition, module, FMOp::DEPTH_PARAM, 0.0, 1.0, 0.5));
+ addParam(ParamWidget::create<Knob26>(depthParamPosition, module, FMOp::DEPTH_PARAM, 0.0, 1.0, 0.0));
addParam(ParamWidget::create<Knob26>(feedbackParamPosition, module, FMOp::FEEDBACK_PARAM, 0.0, 1.0, 0.0));
addParam(ParamWidget::create<Knob26>(levelParamPosition, module, FMOp::LEVEL_PARAM, 0.0, 1.0, 1.0));
addParam(ParamWidget::create<StatefulButton9>(envToLevelParamPosition, module, FMOp::ENV_TO_LEVEL_PARAM, 0.0, 1.0, 0.0));
diff --git a/src/FMOp.hpp b/src/FMOp.hpp
@@ -48,12 +48,14 @@ struct FMOp : Module {
NUM_LIGHTS
};
+ const float amplitude = 5.0f;
const int modulationSteps = 100;
int _steps = 0;
float _baseHZ = 0.0f;
float _feedback = 0.0f;
float _depth = 0.0f;
float _level = 0.0f;
+ bool _envelopeOn = false;
bool _levelEnvelopeOn = false;
bool _feedbackEnvelopeOn = false;
ADSR _envelope;
diff --git a/src/Test.cpp b/src/Test.cpp
@@ -151,29 +151,48 @@ void Test::step() {
}
#elif FM
+ const float amplitude = 5.0f;
float baseHz = oscillatorPitch();
float ratio = ratio2();
- _modulator.setSampleRate(engineGetSampleRate());
- _modulator.setFrequency(baseHz * ratio2());
- float hz = _modulator.next() * index3() * ratio * baseHz;
- _carrier.setSampleRate(engineGetSampleRate());
- _carrier.setFrequency(baseHz + hz);
- outputs[OUT_OUTPUT].value = _carrier.next() * 5.0f;
+ float index = index3();
+ float sampleRate = engineGetSampleRate();
+ if (_baseHz != baseHz || _ratio != ratio || _index != index || _sampleRate != sampleRate) {
+ _baseHz = baseHz;
+ _ratio = ratio;
+ _index = index;
+ _sampleRate = sampleRate;
+ float modHz = _ratio * _baseHz;
+ // printf("baseHz=%f ratio=%f modHz=%f index=%f\n", _baseHz, _ratio, modHz, _index);
+
+ _modulator.setFrequency(modHz);
+ _modulator.setSampleRate(_sampleRate);
+ _carrier.setSampleRate(_sampleRate);
+
+ _carrier2.setSampleRate(engineGetSampleRate());
+ _carrier2.setFrequency(baseHz);
+ _modulator2.setSampleRate(engineGetSampleRate());
+ _modulator2.setFrequency(modHz);
+ }
- // _modulator2.setSampleRate(engineGetSampleRate());
- // _modulator2.setFrequency(baseHz * ratio2());
- // float hz2 = _modulator2.next() * index3() * ratio * baseHz;
- // _carrier2.setSampleRate(engineGetSampleRate());
- // _carrier2.setFrequency(std::max(baseHz + hz2, 0.0f));
- // outputs[OUT2_OUTPUT].value = _carrier2.next() * 5.0f;
+ // linear FM.
+ float modHz = _ratio * _baseHz;
+ _carrier.setFrequency(_baseHz + _index * _modulator.next() * modHz); // linear FM requires knowing the modulator's frequency.
+ outputs[OUT_OUTPUT].value = _carrier.next() * amplitude;
+
+ // PM for comparison - identical output.
+ _carrier2.advancePhase();
+ outputs[OUT2_OUTPUT].value = _carrier2.nextFromPhasor(_carrier2, Phasor::radiansToPhase(_index * _modulator2.next())) * amplitude;
#elif PM
+ const float amplitude = 5.0f;
+ float baseHz = oscillatorPitch();
+ float modHz = ratio2() * baseHz;
_carrier.setSampleRate(engineGetSampleRate());
- _carrier.setFrequency(oscillatorPitch());
+ _carrier.setFrequency(baseHz);
_modulator.setSampleRate(engineGetSampleRate());
- _modulator.setFrequency(_carrier._frequency * ratio2());
+ _modulator.setFrequency(modHz);
_carrier.advancePhase();
- outputs[OUT_OUTPUT].value = _carrier.nextFromPhasor(_carrier, Phasor::radiansToPhase(index3() * _modulator.next())) * 5.0f;
+ outputs[OUT_OUTPUT].value = _carrier.nextFromPhasor(_carrier, Phasor::radiansToPhase(index3() * _modulator.next())) * amplitude;
#elif FEEDBACK_PM
_carrier.setSampleRate(engineGetSampleRate());
diff --git a/src/Test.hpp b/src/Test.hpp
@@ -9,12 +9,12 @@ extern Model* modelTest;
// #define SINE 1
// #define SQUARE 1
// #define SAW 1
-#define SATSAW 1
+// #define SATSAW 1
// #define TRIANGLE 1
// #define SINEBANK 1
// #define OVERSAMPLING 1
// #define OVERSAMPLED_BL 1
-// #define FM 1
+#define FM 1
// #define PM 1
// #define FEEDBACK_PM 1
// #define EG 1
@@ -121,10 +121,14 @@ struct Test : Module {
BandLimitedSawOscillator _saw2;
LowPassFilter _lpf;
#elif FM
+ float _baseHz = 0.0f;
+ float _ratio = 0.0f;
+ float _index = 0.0f;
+ float _sampleRate = 0.0f;
SineTableOscillator _modulator;
SineTableOscillator _carrier;
- // SineTableOscillator _modulator2;
- // SineTableOscillator _carrier2;
+ SineTableOscillator _modulator2;
+ SineTableOscillator _carrier2;
#elif PM
SineTableOscillator _modulator;
SineTableOscillator _carrier;
@@ -173,8 +177,8 @@ struct Test : Module {
#elif FM
, _modulator(44100.0, 1000.0, 1.0)
, _carrier(44100.0, 1000.0, 1.0)
- // , _modulator2(44100.0, 1000.0, 1.0)
- // , _carrier2(44100.0, 1000.0, 1.0)
+ , _modulator2(44100.0, 1000.0, 1.0)
+ , _carrier2(44100.0, 1000.0, 1.0)
#elif PM
, _modulator(44100.0, 1000.0, 1.0)
, _carrier(44100.0, 1000.0)
diff --git a/src/VCO.cpp b/src/VCO.cpp
@@ -56,10 +56,12 @@ void VCO::step() {
_fmLinearMode = ((int)params[FM_TYPE_PARAM].value) == 1;
}
- if (inputs[FM_INPUT].active) {
+ float phaseOffset = 0.0f;
+ if (inputs[FM_INPUT].active && _fmDepth > 0.01f) {
float fm = inputs[FM_INPUT].value * _fmDepth;
if (_fmLinearMode) {
- _phasor.setFrequency(_baseHz + fm * _baseHz);
+ _phasor.setFrequency(_baseHz);
+ phaseOffset = Phasor::radiansToPhase(2.0f * fm);
}
else {
_phasor.setFrequency(cvToFrequency(_baseVOct + fm));
@@ -70,16 +72,16 @@ void VCO::step() {
}
_phasor.advancePhase();
if (outputs[SQUARE_OUTPUT].active) {
- outputs[SQUARE_OUTPUT].value = amplitude * _square.nextFromPhasor(_phasor);
+ outputs[SQUARE_OUTPUT].value = amplitude * _square.nextFromPhasor(_phasor, phaseOffset);
}
if (outputs[SAW_OUTPUT].active) {
- outputs[SAW_OUTPUT].value = amplitude * _saw.nextFromPhasor(_phasor);
+ outputs[SAW_OUTPUT].value = amplitude * _saw.nextFromPhasor(_phasor, phaseOffset);
}
if (outputs[TRIANGLE_OUTPUT].active) {
- outputs[TRIANGLE_OUTPUT].value = amplitude * _triangle.nextFromPhasor(_phasor);
+ outputs[TRIANGLE_OUTPUT].value = amplitude * _triangle.nextFromPhasor(_phasor, phaseOffset);
}
if (outputs[SINE_OUTPUT].active) {
- outputs[SINE_OUTPUT].value = amplitude * _sine.nextFromPhasor(_phasor);
+ outputs[SINE_OUTPUT].value = amplitude * _sine.nextFromPhasor(_phasor, phaseOffset);
}
}
@@ -126,7 +128,7 @@ struct VCOWidget : ModuleWidget {
addParam(ParamWidget::create<Knob16>(fineParamPosition, module, VCO::FINE_PARAM, -1.0, 1.0, 0.0));
addParam(ParamWidget::create<StatefulButton9>(slowParamPosition, module, VCO::SLOW_PARAM, 0.0, 1.0, 0.0));
addParam(ParamWidget::create<Knob26>(pwParamPosition, module, VCO::PW_PARAM, -1.0, 1.0, 0.0));
- addParam(ParamWidget::create<Knob26>(fmParamPosition, module, VCO::FM_PARAM, -1.0, 1.0, 0.0));
+ addParam(ParamWidget::create<Knob26>(fmParamPosition, module, VCO::FM_PARAM, 0.0, 1.0, 0.0));
addParam(ParamWidget::create<StatefulButton9>(fmTypeParamPosition, module, VCO::FM_TYPE_PARAM, 0.0, 1.0, 0.0));
addInput(Port::create<Port24>(pitchInputPosition, Port::INPUT, module, VCO::PITCH_INPUT));
diff --git a/src/XCO.cpp b/src/XCO.cpp
@@ -73,10 +73,12 @@ void XCO::step() {
_sineMix = level(params[SINE_MIX_PARAM], inputs[SINE_MIX_INPUT]);
}
- if (inputs[FM_INPUT].active) {
+ float phaseOffset = 0.0f;
+ if (inputs[FM_INPUT].active && _fmDepth > 0.01f) {
float fm = inputs[FM_INPUT].value * _fmDepth;
if (_fmLinearMode) {
- _phasor.setFrequency(_baseHz + fm * _baseHz);
+ _phasor.setFrequency(_baseHz);
+ phaseOffset = Phasor::radiansToPhase(2.0f * fm);
}
else {
_phasor.setFrequency(cvToFrequency(_baseVOct + fm));
@@ -88,16 +90,16 @@ void XCO::step() {
_phasor.advancePhase();
float mix = 0.0f;
if (outputs[MIX_OUTPUT].active || outputs[SQUARE_OUTPUT].active) {
- mix += outputs[SQUARE_OUTPUT].value = amplitude * _squareMix * _square.nextFromPhasor(_phasor, _squarePhaseOffset);
+ mix += outputs[SQUARE_OUTPUT].value = amplitude * _squareMix * _square.nextFromPhasor(_phasor, _squarePhaseOffset + phaseOffset);
}
if (outputs[MIX_OUTPUT].active || outputs[SAW_OUTPUT].active) {
- mix += outputs[SAW_OUTPUT].value = amplitude * _sawMix * _saw.nextFromPhasor(_phasor, _sawPhaseOffset);
+ mix += outputs[SAW_OUTPUT].value = amplitude * _sawMix * _saw.nextFromPhasor(_phasor, _sawPhaseOffset + phaseOffset);
}
if (outputs[MIX_OUTPUT].active || outputs[TRIANGLE_OUTPUT].active) {
- mix += outputs[TRIANGLE_OUTPUT].value = amplitude * _triangleMix * _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset);
+ mix += outputs[TRIANGLE_OUTPUT].value = amplitude * _triangleMix * _triangle.nextFromPhasor(_phasor, _trianglePhaseOffset + phaseOffset);
}
if (outputs[MIX_OUTPUT].active || outputs[SINE_OUTPUT].active) {
- mix += outputs[SINE_OUTPUT].value = amplitude * _sineMix * _sine.nextFromPhasor(_phasor, _sinePhaseOffset);
+ mix += outputs[SINE_OUTPUT].value = amplitude * _sineMix * _sine.nextFromPhasor(_phasor, _sinePhaseOffset + phaseOffset);
}
if (outputs[MIX_OUTPUT].active) {
outputs[MIX_OUTPUT].value = mix;