Skip to content

Releases: gresade/preenfm3

preenfm3 v1.06g — HQ oscillator interpolation & randomizer improvements

03 Jun 06:21

Choose a tag to compare

HQ Oscillator Mode

A new HQ oscillator quality tier is now available in the engine decimation setting, above Full.

When HQ is selected, linear interpolation is applied at the wavetable lookup site for all six oscillators. This reduces the stair-step aliasing that can appear in high-partial FM spectra, particularly on complex algorithms with multiple carrier operators. HQ also works correctly with the Warp parameter — the warped-phase and interpolation transforms are combined into a single lookup without quality loss.

HQ is mutually exclusive with wave decimation: if a decimation mode is active, interpolation is suppressed and the decimated output is used instead.


Preset Randomizer Improvements

The randomizer (Oper / EnvT / IM / Modl) has received a large number of tuning and feature improvements.

Oscillator waveforms

  • Percussive mode (EnvT = perc) can now occasionally pick user waveforms (USER1–USER6) for individual operators (~10% probability), adding custom transient color to drum and percussive patches.
  • Pad mode (EnvT = pad) now draws from the full waveform palette with a strong sine bias (85% sine, 15% everything else), rather than being restricted to sine/saw/triangle.

Percussive envelope and LFO timing

  • Percussive sustain and release times have been tightened (sustain max 0.5 s, release max 2.5 s) so randomized drum patches no longer have unrealistically long tails.
  • Percussive LFO one-shot frequencies were raised (floor 3 Hz, ceiling 10 + 3×Modl Hz) so transient modulation — pitch drops, filter sweeps — completes well within the shorter envelope window.

Pad LFO improvements

  • Pad LFOs can now use slow multi-shot sync modes (2–8 cycles, 40% probability), so modulation evolves and then settles rather than cycling indefinitely.
  • Pad LFOs can carry a non-zero bias offset (±0.3) for asymmetric movement. This is only applied when the LFO is not routed to amplitude-sensitive destinations (MIX_OSC*, ALL_MIX, FILTER*_AMP). If the randomizer assigned an unsafe destination, it is automatically rerouted to a safe one before the offset is enabled.

Stability fixes

  • All releaseLevel values are now forced to 0.0, preventing voices from dying at non-zero amplitude (clicks) and closing an edge case where a modulator envelope could loop indefinitely.
  • Output mixer gain is normalized by the algorithm's carrier count, keeping the summed output level consistent regardless of whether the algorithm has 1 or 6 carriers.

feat: operator Phase & Warp, mod-matrix destinations, CPU optimisation (v1.06f)

30 May 08:42

Choose a tag to compare

Operator Phase & Warp

  • Add per-operator Warp parameter (±4 range, values beyond ±1 invert one
    half-cycle). Implemented in getNextSample / getNextBlock / getNextBlockHQ
    including the decimated sub-paths. A deadband near zero preserves the fast
    no-warp code path under near-zero modulation.
  • Per-operator start Phase offset retained and wired; oscilloscope phase marker
    visualisation added (FirmwareTftDisplay).
  • Patch format bumped to v1.5; Warp defaults to 0 on load of older patches,
    out-of-range values clamped on load (PreenFMFileType.cpp).

Modulation matrix

  • New destinations OSC1..6_PHASE and OSC1..6_WARP added to DestinationEnum
    (Common.h).
  • Phase applied at note-on; Warp updated every block in
    Voice::prepareMatrixForNewBlock via updateWarpWithMatrix (Voice.cpp / Osc.h).
  • Matrix destination labels o1Ph–o6Ph and o1Wr–o6Wr wired in FMDisplayEditor.

MIDI

  • CC_OSC1..6_WARP mapped per operator (MidiDecoder.cpp / MidiDecoder.h).
  • ENCODER_OSC_WARP encoder row added to editor UI.

Synth engine / init

  • Osc::init now receives phaseParamsBase pointer; each oscillator binds its
    Warp directly to its per-timbre phase row, fixing parameter ownership across
    timbres (Osc.cpp / Timbre.cpp / SynthState.cpp / SynthState.h).

CPU optimisation

  • quantizeOscOutputBeforeEnvelope skipped in non-decimation block-render paths
    (getNextBlock, getNextBlockHQ, getNextSample), removing a branch + two
    multiplies per sample in the common case.
  • waveDecimationEnabled cached once per block call instead of read per sample.
  • Selective #pragma GCC optimize("Ofast","fast-math") applied in Release builds
    only to six hot DSP translation units: Osc.cpp, Voice.cpp, FxBus.cpp,
    TimbreFx.cpp, SimpleComp.cpp, SimpleEnvelope.cpp.
  • Release makefile: -O2 for DebugLQFP144; LTO enabled for Release link.

Versioning

  • firmware version bumped to v1.06f (version.h).

Documentation

  • README.md updated with full description of Phase, Warp, mod-matrix
    destinations, CPU optimisation, and patch migration notes.

Added LFO Delay (dial LFO phase in negative direction)

27 May 18:00

Choose a tag to compare

LFO Phase encoder: add startup delay mode + fix float32 stuck at 100ms

Negative LFO Phase values (0 to -4) now function as a per-LFO startup
delay displayed in integer milliseconds (0–4000 ms). Positive values
retain the existing phase-offset behaviour (0–1).

Changes (versus previous release v1.06c):

  • Add DISPLAY_TYPE_LFO_DELAY: negative values rendered as ms integers
    with fixed-width padding; positive values use printFloatWithSpace.
  • Custom piecewise encoder stepping for ROW_LFOPHASES (encoders 0–2):
    adaptive resolution — 1 ms steps below 50 ms, 2 ms to 100 ms,
    5 ms to 200 ms, 10 ms to 1 s, 20 ms to 2 s, 25 ms to 3 s,
    50 ms to 4 s.
  • Fix float32 forward-progress guard: 20 × f32(0.005) underflows
    slightly below 0.1, causing delayStep() to return 0.002 and
    re-quantise back to q=20 indefinitely. If the quantised result
    does not advance the value, force -(q+1)×quantStep instead.
  • Bump version to v1.06e.
  • Update README Branch Notes with LFO delay description.

Add one-shot LFO sync modes, expand LFO shapes, and optimize FM decimation

26 May 20:22

Choose a tag to compare

FM decimation reworked for performance and consistency:

  • cap selectable precision to 1..19 bit plus Full mode
  • remove 20..24 bit modes from engine enum/UI path
  • simplify quantization path by removing expensive phase-accumulator quantization
  • add D=2 temporal sample-and-hold behavior in decimated oscillator render paths
  • keep feedback rendering full-rate for stability
  • precompute per-voice decimation scale/invScale and use saturating fixed-point-style rounding

Preset/patch compatibility and format updates:

  • bump patch format to 1.4
  • add dedicated per-LFO sync-mode storage in synth params/flash params
  • persist/restore new sync-mode fields in flash conversion
  • for older patches, derive sync mode from legacy internal/external frequency behavior
  • clamp legacy decimation values 20..24 to 19-bit on load
  • clamp sync mode values to valid range on load

LFO sync system expanded:

  • add sync mode model: Int, Ext, and one-shot 1..8 cycles for both internal/external clocks
  • decouple one-shot behavior from KSync so they can be combined
  • add one-shot runtime handling (remaining-cycle counter, terminal hold value, shape-aware terminal behavior)
  • prevent one-shot retrigger during overlapping mono/unison glide note transitions

LFO engine and shape expansion:

  • extend LFO shape enum/editor/renderer with new shapes:
    • SawDown, DecayExp, DecayLog, RiseExp, RiseLog, AttackDecay, AttackHoldDecay, S-Decay, Buchla Plong variants, SinSquare, SinZero, SinPos, and User1..User6
  • refactor shape execution into dedicated executors with precomputed constants
  • add curve lookup table header for exp/log/damping tables used by new LFO shapes

UI/oscilloscope updates:

  • extend sync selector labels and encoder handling for new sync modes
  • update LFO oscilloscope preview to render all new shapes
  • add one-shot hold visualization after the configured shot count
  • update LFO oscilloscope parameter passing to include sync mode

Misc updates:

  • move TFT display instance to RAM_D1 section
  • bump firmware version string to v1.06c
  • update CLI build script to derive firmware/bootloader versions from headers and generate release names dynamically (with safe fallbacks)
  • refresh README branch notes to reflect decimation and LFO sync/shape behavior changes

Phase offet (working, drawn) and Improved DX7 import

25 May 09:18

Choose a tag to compare

Phase offset working, is now drawn on the Osc osscilloscope view as a line and point of intersection (tested)
Phase is reset on note on, if not in slide mode with overlapping notes.
The phase offset allows for waveshaping, when frequency is set to 0! Use it on the Osc/Operator which is being modulated.
Import has been improved for DX7 patches using also the Modulation Matrix.

  • Phase offset parameter on 1st Oscillator page
  • Update build script to refresh release artifacts automatically after a successful build
  • Add script options to configure release output paths and artifact names
  • Regenerate release checksums and rebuild release zip from the script workflow
  • Import DX7 oscillator phase behavior using key-sync approximation
  • Set all operator phases to 0 degrees on import when DX7 key sync is ON
  • Preserve existing operator phase settings on import when DX7 key sync is OFF

per-operator phase offset & DX7 import

25 May 00:40

Choose a tag to compare

Pre-release

Add per-operator phase offset (note-on start phase) with UI + preset compatibility

  • add a dedicated phase offset parameter for each operator
  • apply operator phase offset on note-on in voice init path
  • add operator-page UI state to edit phase offset with short labels
  • keep editor architecture intact by using dedicated operator phase rows
  • extend runtime and flash parameter structs for phase persistence
  • update save/load conversion for new phase parameters
  • preserve compatibility for existing/legacy patches (including v2 layout handling)
  • update built-in preset initializers with default phase entries
  • add/refresh code comments in changed areas for maintainability

DX7 import: validate sysex bank and align importer mapping

  • harden DX7 sysex loading by reading full 32-voice bank frames and validating:
    • frame size and header/footer bytes
    • MIDI channel nibble format
    • 7-bit payload/checksum bytes
  • Yamaha checksum integrity
  • reject invalid patch indices and invalid bank data before import.
  • add null-safe DX7 patch loading path in synth state to avoid loading invalid pointers.
  • rework DX7 feedback import:
    • map DX7 feedback to modulationIndex6 (capped at 0.9)
    • remove legacy fb>4 saw-shape workaround branches
  • fix transpose import branch ordering so deep negative transpose is reachable.
  • fix AM depth regression:
    • preserve row 2 AMD-derived modulation amount
    • add regression guard comment
  • improve IM velocity split mapping with updated KVS ratios and apply split only when KVS > 0.
  • import DX7 LFO waveform and key sync in importer:
    • map DX7 wave shapes to internal LFO shapes
    • map key sync state into keybRamp sign semantics
    • deduplicate shared LFO import assignment logic
  • clamp DX7 PMD/AMD to 0..99 before scaling/table lookup.
  • add compile-time table-size guard for DX7 AMD lookup table.
  • fix LFO switch-case scope issue in header after key-sync update.
  • refresh and correct README/docs wording and links.

hints:

  • rename firmware and bootloader files to shorter name
  • bootloader untested

DX7 Import improved, Operator Phase, Operator descimation

25 May 12:09
65cb963

Choose a tag to compare

Functional additions and fixes in this branch include:

  • Per-operator start phase offset support in the synth engine.
  • Operator phase marker visualization and editor/UI wiring for phase offsets.
  • DX7 SysEx import hardening and mapping fixes (bank validation, vibrato/AMS handling, fixed-frequency preservation).
  • FM decimation control added to engine parameters with preset/file persistence support.
  • FM decimation expanded in oscillator DSP path to multiple domains: FM sum, phase increment/accumulator, phase modulation index/offset, waveform input, and oscillator output level before envelope multiply.
  • High-quality wavetable interpolation is bypassed when decimation is enabled.
  • bootloader unchanged and not tested
  • rename binary to shorter name if necessary for SD card