@@ -267,6 +267,11 @@ struct FM16Seq : Module, QuantizeUtils {
267267 return quantizedVolts * 12 .f ;
268268 }
269269
270+ // Quantizes total volts (step pitch + CV), not just the stored step pitch.
271+ float quantizePitchVolts (float volts) {
272+ return closestVoltageInScale (volts, pitchQuantizeRoot, pitchQuantizeScale);
273+ }
274+
270275 void quantizeStoredPitches () {
271276 for (int i = 0 ; i < STEPS ; i++) {
272277 stepData[i].pitch = quantizePitchSemitones (stepData[i].pitch );
@@ -818,6 +823,7 @@ struct FM16Seq : Module, QuantizeUtils {
818823 ScopedLocalRngSeed scopedSeed (inputs[SEED_INPUT ].isConnected (), inputs[SEED_INPUT ].getVoltage ());
819824
820825 bool stepChanged = false ;
826+ bool stepButtonPressed = false ;
821827
822828 // Check for step selection changes.
823829 // Editing should always allow selecting any of the 16 stored steps,
@@ -830,6 +836,7 @@ struct FM16Seq : Module, QuantizeUtils {
830836 loadEditorFromSelectedStep ();
831837 stepChanged = true ;
832838 }
839+ stepButtonPressed = true ; // Clicking a step button also triggers that step
833840 }
834841 }
835842 if (!stepChanged) {
@@ -838,7 +845,7 @@ struct FM16Seq : Module, QuantizeUtils {
838845
839846 bool manualTriggerPressed = manualStepTrigger.process (params[MANUAL_STEP_TRIGGER_PARAM ].getValue ());
840847 bool manualGateTriggered = manualStepGateTrigger.process (inputs[MANUAL_STEP_GATE_INPUT ].getVoltage ());
841- bool manualTriggerEvent = manualTriggerPressed || manualGateTriggered;
848+ bool manualTriggerEvent = manualTriggerPressed || manualGateTriggered || stepButtonPressed ;
842849 if (manualTriggerEvent) {
843850 for (int i = 0 ; i < sequenceLength; i++) {
844851 if (engines[i].gateHeld ) {
@@ -1079,9 +1086,11 @@ struct FM16Seq : Module, QuantizeUtils {
10791086 float modRatioCV = getStepCvVoltage (MOD_RATIO_INPUT );
10801087 float carRatioCV = getStepCvVoltage (CAR_RATIO_INPUT );
10811088
1082- // Combine CV with stored step parameters
1083- float quantizedStepPitch = pitchQuantizeEnabled ? quantizePitchSemitones (s.pitch ) : s.pitch ;
1084- float baseFreqCV = pitchVolts + (quantizedStepPitch / 12 .f );
1089+ // Combine CV with stored step parameters, then quantize the combined pitch
1090+ float baseFreqCV = pitchVolts + (s.pitch / 12 .f );
1091+ if (pitchQuantizeEnabled) {
1092+ baseFreqCV = quantizePitchVolts (baseFreqCV);
1093+ }
10851094 engine.baseFreq = 261 .6256f * std::pow (2 .f , baseFreqCV);
10861095
10871096 float carRatio = s.carRatio + carRatioCV;
@@ -1175,11 +1184,13 @@ struct FM16Seq : Module, QuantizeUtils {
11751184 outputs[CARRIER_OUTPUT ].setVoltage (carrierOut);
11761185 outputs[CARRIER_OUTPUT ].setChannels (1 );
11771186
1178- // V/Oct output
1187+ // V/Oct output: quantize the combined pitch (step + CV), not just the step pitch
11791188 const StepData& s = stepData[latchedStep];
1180- float quantizedStepPitch = pitchQuantizeEnabled ? quantizePitchSemitones (s.pitch ) : s.pitch ;
11811189 float pitchVolts = inputs[PITCH_INPUT ].isConnected () ? inputs[PITCH_INPUT ].getVoltage (cvChannel) : 0 .f ;
1182- float voctOut = pitchVolts + (quantizedStepPitch / 12 .f );
1190+ float voctOut = pitchVolts + (s.pitch / 12 .f );
1191+ if (pitchQuantizeEnabled) {
1192+ voctOut = quantizePitchVolts (voctOut);
1193+ }
11831194 outputs[V_OCT_OUTPUT ].setVoltage (voctOut);
11841195 outputs[V_OCT_OUTPUT ].setChannels (1 );
11851196
0 commit comments