SSPACEAGEDOCUMENTATION/
DSP and Instruments

80s FM Reference Comparison

Updated Aug 30, 2026   |   23.4 KB   |   docs/FM_REFERENCE_COMPARISON.md

80s FM Reference Comparison

FmReferenceCompare compares an 80s FM WAV render with a reference render from Dexed or another DX7-compatible instrument. It compensates for a small start-time offset and overall gain before measuring the difference.

Render both files with the same MIDI note, velocity, duration, sample rate, and patch. Disable effects and normalization in both instruments. Then run:

FmReferenceCompare.exe spaceage.wav dexed.wav

The audio self-test executable can create the SpaceAge side deterministically from a bank or single-voice SysEx file. Set these environment variables before running it:

SPACEAGE_FM_REFERENCE_RENDER_ONLY=1
SPACEAGE_FM_REFERENCE_SYSEX=C:\path\bank.syx
SPACEAGE_FM_REFERENCE_VOICE=0
SPACEAGE_FM_REFERENCE_NOTE=60
SPACEAGE_FM_REFERENCE_VELOCITY=0.7874015748
SPACEAGE_FM_REFERENCE_OUTPUT=C:\path\spaceage.wav

Voice indexes are zero-based. The renderer uses 48 kHz stereo, holds the note for exactly two seconds, renders three seconds of release, and disables shared effects. Match those settings when creating the Dexed reference WAV.

When Dexed is installed, its side of the comparison can also be rendered without opening its editor:

FmDexedReferenceRender.exe Dexed.vst3 patch.syx dexed.wav 0 60 0.7874015748

The optional final values are the zero-based voice index, MIDI note, and normalized velocity. The renderer sends the user-provided SysEx to the external Dexed plug-in and records its output; Dexed code is not linked into SpaceAge.

The optional third argument sets the maximum alignment range in milliseconds:

FmReferenceCompare.exe spaceage.wav dexed.wav 250

The tool reports JSON containing:

  • lagSamples and lagMilliseconds: detected timing offset.
  • candidateGain: gain applied to the SpaceAge render for comparison.
  • correlation: waveform similarity, where 1.0 is identical in shape.
  • normalizedErrorRms: residual error relative to the reference RMS.
  • peakError: largest individual sample error after alignment and gain matching.
  • spectralCentroidDeltaHz: broad brightness difference; positive means the SpaceAge render is brighter over the analyzed opening window.
  • logSpectrumCorrelation: gain-normalized similarity of the complete opening log-magnitude spectrum. This is more useful than raw waveform correlation for phase-sensitive feedback patches; 1.0 indicates the same spectral shape.
  • logSpectrumErrorRms: RMS distance between the mean-centered opening log-magnitude spectra. Lower values indicate a closer timbral match.
  • releaseAlignedCorrelation: release-only waveform correlation after an independent local timing alignment.
  • releaseEnergyRatio: SpaceAge release RMS relative to Dexed after applying the whole-note gain match; 1.0 indicates equal release energy.
  • releaseEnvelopeCorrelation: carrier-independent similarity between the two release amplitude contours, measured from short RMS windows.

Run FmReferenceCompare.exe --self-test to verify the comparator itself. This tool is deliberately external to the synth and contains no Dexed source code.

Measured Baseline - 2026-08-30

The current 80s FM engine was compared with the locally installed Dexed VST3 at 48 kHz using Dexed_01.syx, MIDI note C4, velocity 100, a two-second hold, and a three-second release. Effects and normalization were disabled. Correlation is reported after timing and gain alignment.

Voice Patch DX7 algorithm Correlation Log-spectrum correlation Spectral centroid delta
1 Say Again. 32 0.9964 0.9998 -111.0 Hz
2 LAURIE 15 0.6930 0.9790 +9.2 Hz
16 FLEXI 4 3 0.9986 0.9902 +924.2 Hz
32 LFO SWEEP 17 0.9947 0.9997 -4142.9 Hz

Segmented correlation helps distinguish envelope/onset differences from steady operator behavior:

Voice Attack Sustain Release
1 0.9991 0.9954 0.9950
2 0.7840 0.6871 0.7331
16 0.9973 0.9997 0.9997
32 0.9984 0.9934 0.9965

This establishes that cartridge parsing and several algorithm paths already track Dexed closely, but fidelity is not yet uniform. The remaining baseline outlier is LAURIE on algorithm 15. Global tonal compensation is not justified because the error changes direction and magnitude by patch.

The same pass verified the algorithm 4 and 6 routing flags against current Dexed. An earlier comparison against the older Google MSFA table briefly suggested 0x41 for the first operator in those rows, but current Dexed uses 0xc1; SpaceAge therefore retains 0xc1 because the installed current Dexed build is the compatibility and audio-comparison target. A follow-up source comparison found that current Dexed rounds non-zero keyboard-level-scaling groups away from the breakpoint, unlike the older Google MSFA implementation; SpaceAge now follows current Dexed because that is the compatibility and audio comparison target. Neither correction regressed the four-voice baseline. A direct transplant of the older reference core's fixed-point ratio-detune constant was also tested and rejected: with SpaceAge's current oscillator-frequency path it reduced voice 2 correlation from 0.6318 to 0.1121 and voice 32 from 0.9878 to 0.9134. The existing pitch-aware detune calculation was restored. This is a measured compatibility decision, not an assumption that the reference formula is incorrect in its native core.

Direct routing coverage was then added with three more voices from the same cartridge:

Voice Patch DX7 algorithm Correlation Attack Sustain Release
6 Lavitar 4 0.9271 0.9441 0.9702 0.9901
8 OB Genviv 6 0.9497 0.9772 0.9912 0.9754
23 CASCADE 21 6 0.9934 0.9777 0.9955 0.9955

These results confirm the current-Dexed algorithm 4 and 6 topologies. Voice 8 initially exposed an amplitude-modulated release failure: disabling only LFO amplitude depth raised release-envelope correlation from 0.4463 to 0.9948. Inspection then found that voice retirement depended on the instantaneous AM-modulated carrier level. A deep LFO trough could therefore retire a voice whose carrier envelopes were still active. Retirement now follows the actual carrier envelope stages. OB Genviv's release correlation improved from 0.4412 to 0.9754 and its release-envelope correlation improved from 0.6786 to 0.9897. With LFO key sync forced in both renderers, release-envelope correlation reaches 0.9992. A focused regression holds a long release through maximum square-wave AM while the existing completed-envelope test still proves eventual retirement. The older fallback that retired an 80s FM voice after 4,096 consecutive quiet release samples was removed as part of the same correction. Cancellation, operator combinations, or modulation can legitimately produce temporary silence; carrier envelope stages now own normal voice lifetime, with only the separate 300-second corruption guard remaining as a final failsafe.

The neutral post-processing path was also made genuinely transparent for 80s FM. Previously, Drive at 0 dB and Bit Depth at 16 still passed the signal through tanh and quantization. That unnecessarily altered bright imported-DX7 transients. Bypassing those operations only when both controls are neutral improved full-render correlation for Lavitar from 0.9271 to 0.9389, OB Genviv from 0.9264 to 0.9422, and CASCADE 21 from 0.9934 to 0.9973.

FLEXI 4's attack mismatch was then isolated branch by branch. Its fixed-frequency branch measured 0.9990 overall and 0.9975 during attack, while its ratio branch fell to 0.3669 during attack. A bare ratio carrier returned to 0.9982 attack correlation, identifying operator 2 as the source. The cartridge stores Fine 127 for that operator, outside the documented DX7 0..99 range. Dexed honors the full 7-bit field while SpaceAge had clamped it to 99. SpaceAge now preserves Fine values through 127 during packed and unpacked SysEx import, runtime synthesis, state storage, and export. The unmodified patch now measures 0.9986 overall, 0.9973 attack, 0.9997 sustain, and 0.9997 release. Values 100..127 are retained for real-world Dexed cartridge compatibility.

LAURIE's algorithm 15 outlier was also isolated by carrier branch. Muting the operator 1 branch left the operator 3 branch at 0.8768 overall, while a bare operator 1 carrier with its operator 2 modulator muted measured 0.9923 overall and 0.9997 during attack. The lower full-patch waveform correlation is therefore concentrated in the operator 2 feedback-modulation path; it is not evidence of an incorrect algorithm topology, cartridge decode, or carrier tuning. LAURIE uses feedback 7, where tiny phase differences rapidly decorrelate sample-level waveforms. Disabling feedback in both renderers raised the full-patch result to 0.8422 and produced a 0.9925 first-10-ms attack match.

The feedback implementation was checked against current Dexed's Apache-2.0 MSFA core. SpaceAge now expresses the same convention directly: the stored feedback shift is 8 - feedback, and the two-sample feedback sum receives the kernel's additional one-bit shift. This preserves the prior effective gain while making the implementation auditable against Dexed. Ratio-operator detune now also follows Dexed's integer conversion order by truncating the updated Q24 log frequency instead of rounding a detached delta. The complete FM core and DX7 SysEx export/import regression tests pass after both corrections. Further LAURIE work should compare feedback spectra and envelopes rather than treating raw long-window correlation as a reliable quality metric for chaotic feedback. The gain-normalized log-spectrum comparison now performs that check. LAURIE measures 0.9790 spectral-shape correlation, only +9.2 Hz centroid delta, and 0.9878 release-envelope correlation despite its 0.6930 raw waveform score. The same metric remains discriminating rather than automatically generous: the three high-waveform-correlation controls measure from 0.9902 to 0.9998. No production correction is justified by the remaining LAURIE phase difference.

That investigation then exposed a concrete triangle-LFO signedness bug. Dexed right-shifts its unsigned Q32 phase before applying the triangle fold; SpaceAge had cast the phase to signed first, causing an arithmetic shift throughout the upper half-cycle. SpaceAge now follows the unsigned operation order. With operator 2 temporarily exposed as a carrier, correlation improved from 0.9926 to 0.9983 and sustain correlation reached 0.9991. LAURIE's full-patch spectral centroid delta improved from roughly -83 Hz to +9 Hz, while Say Again and LFO SWEEP also improved to 0.9964 and 0.9947 overall. The focused 80s FM core suite passes after the correction. The production triangle fold now lives in the shared Dx7Math.h helper with compile-time checks at phase origin, both quarter cycles, the half cycle, and wraparound. Saw up/down, square, sine, and sample-and-hold were audited for the same signed-phase error and already match Dexed's unsigned Q32 phase handling.

LFO Waveform Matrix - 2026-08-30

LAURIE's operator 2 was temporarily exposed as a carrier on algorithm 32 while retaining its pitch-modulation depth, sensitivity, speed, and delay. LFO key sync was forced in both renderers so free-running startup phase could not skew the result. Only the waveform field changed between rows.

Waveform Correlation Attack Sustain Release envelope
Triangle 0.999970463 0.999968199 0.999970783 0.999988914
Saw down 0.999970465 0.999968340 0.999970561 0.999988623
Saw up 0.999970471 0.999967889 0.999971022 0.999989071
Square 0.999970330 0.999967644 0.999971033 0.999980666
Sine 0.999970441 0.999967757 0.999971001 0.999987035
Sample and hold 0.999969975 0.999968227 0.999969994 0.999930218

All six SpaceAge renders have distinct hashes. This verifies that waveform selection is active and that pitch modulation, LFO delay, phase progression, and each waveform shape track the installed Dexed reference closely.

Amplitude-Modulation Matrix - 2026-08-30

Six cartridge voices with both non-zero LFO amplitude depth and operator AMS were compared. The release-envelope metric removes carrier phase and compares short-window RMS contours.

Voice Patch Algorithm AMD Maximum AMS Release envelope
8 OB Genviv 6 99 3 0.9897
10 LFORez ++ 4 85 3 0.9805
19 TRW 23 36 2 0.9911
21 DEVIL TACK 8 99 2 0.9998
26 -WOBBLE 1- 7 25 2 0.9988
32 LFO SWEEP 17 8 1 0.9996

The original OB Genviv result exposed a voice-lifecycle error rather than an amplitude-modulation coefficient error. A carrier sampled near a deep AM trough could set the voice's completed flag before its operator envelope had actually finished. Completion now follows carrier envelope stages and can return to false after an AM trough. Its release correlation improved from 0.4412 to 0.9754 and its release-envelope correlation improved from 0.6786 to 0.9897. All six matrix cases now track closely, including both voices with AMD 99.

Both reference renderers retain diagnostic-only environment switches for disabling amplitude modulation or forcing LFO key sync. These switches are used only by offline comparisons and do not alter application behavior.

The same validation also confirmed that cartridge path, selected voice, and bank-aware browser labels survive lane changes and a complete project-state save/load with a newly constructed processor and editor.

Cartridge Release Sweep - 2026-08-30

All 32 voices in Dexed_01.syx were loaded independently, triggered for 50 ms, and rendered for up to 60 seconds through the production 80s FM path. Every voice produced finite, audible output, remained bounded to its single allocated voice, and retired naturally. Programmed lifetime ranged from 64 ms to 44.853 seconds; no voice remained active at the test limit.

This sweep validates the removal of the older generic silence watchdog. DX7 patches can legitimately contain very long envelopes, while amplitude modulation can create temporary near-silence before the envelope is complete. Voice retirement therefore follows carrier operator-envelope completion rather than a fixed low-amplitude duration. The 300-second corruption guard remains as the final bound for invalid state.

Control-Surface Audit - 2026-08-30

All ten 80s FM pages are now exercised by the native-synth control audit at both 1800x1000 and 1100x800. The engine had previously been absent from that route table. Its operator, global, performance, and controller sliders now use the shared keyboard-focus, accessible-title, velocity-drag, readable-value, and precision-control conventions. The expanded audit reports no 80s FM geometry, target-size, label-clearance, interaction, accessibility, or undo failure. The complete focused native-synth gate passes with 1,130 of 1,130 visible parameter controls accessible and no undersized targets or collisions.

Cartridge Identity and Lane Isolation - 2026-08-30

The cartridge compatibility regression now uses two valid 32-voice DX7 banks whose voices contain distinct algorithm values. It proves that selecting voice 64 applies its exact parameter data, Next wraps to voice 1 and applies that voice, and Previous wraps back to voice 64 and restores its data. This replaces the earlier weak fixture in which every voice was effectively identical.

The same test places a different 80s FM algorithm on a neighboring instrument lane before browsing. Cartridge selection and stepping must leave that lane unchanged. A complete project save/load must then restore both lanes' parameter values as well as the 64-entry cartridge browser, bank-aware labels, and selected voice. The regression passes against Dexed_01.syx, including the synthetic two-bank, checksumless-bank, and wrapped-single-voice compatibility fixtures.

Cartridge stepping no longer rebuilds the complete synth-editor attachment graph after every selection. Imported parameters continue through their normal attachments, while the 80s FM controls are synchronized directly for immediate visual accuracy. Lane-owned imports also avoid rebuilding the unrelated drum-pad surface and duplicate piano/arrangement selectors. On the focused Dexed_01.syx benchmark, median browsing time for 32 voices fell from 1,343.51 ms (41.98 ms per voice) to 597.38-625.04 ms (18.67-19.53 ms per voice), a 53.5-55.5% reduction across final runs. Exact voice data, visible Algorithm values, neighboring-lane isolation, project reload, and the full synth-control and FM sound-core regressions all pass after the change.

The shared editor parameter setter and native factory reset now suppress host notifications only when a parameter is already at the requested normalized value. Real changes retain the normal host notification and undo path. The factory recall regression passes across all 14 covered native engines with visible-control recall and undo intact.

Cartridge Packaging Recovery - 2026-08-30

The compatibility suite now proves parser behavior for concatenated multi-bank dumps, arbitrary leading and trailing bytes, .sysx naming, a wrapped single voice following two banks, raw 4,096-byte bank payloads, checksumless historical banks, mixed corrupt/valid banks, and corrupt-only files. The padded mixed dump returns all 65 valid voices, the raw bank returns 32, and a corrupt first bank does not hide the 32 valid voices that follow it. A file containing only the damaged bank is rejected with DX7 SYSEX CHECKSUM FAILED.

Partial recovery is no longer silent in the load workflow. When usable voices are retained while one or more recognized messages fail checksum validation, the cartridge-ready status reports the exact number of damaged messages skipped. The focused real-cartridge browser, patch identity, lane isolation, project round-trip, and packaging regressions all pass after this addition.

Portable Cartridge Browser State - 2026-08-30

An 80s FM cartridge path and selected browser voice are now explicit parts of the owning instrument's project record. Complete project archives include the referenced .syx or .sysx file and remap its saved path on import, so another computer can reopen the cartridge browser instead of preserving only the currently loaded voice.

Missing cartridges use the standard Repair Missing Assets workflow. Repairing reconnects the browser file without applying a voice or changing the current FM sound. Clear Without Repair removes only the obsolete cartridge path and browser selection; the imported voice parameters remain intact. The focused missing-asset lifecycle regression passes with this behavior. The complete project-archive regression now carries a synthetic .syx fixture alongside the other instrument assets and proves source-path redaction, seven-resource remapping, selected-voice restoration, unchanged algorithm data, and a second save/reopen on the imported path. Repair and clear also refresh an already-open cartridge browser immediately.

Cartridge repair now validates the replacement's contents as well as its file extension. The shared validity contract accepts raw 4,096-byte banks, standard checksummed banks and voices, historical checksumless banks, and wrapped single voices. A junk file renamed to .syx is rejected, the missing-asset receipt remains available, and the current FM parameters are not changed. The focused repair and archive gates pass with this stricter boundary.

The repair validator and cartridge decoder now share one SysEx message scanner. It owns framing, payload offsets, packed/unpacked voice counts, recognized-message status, and checksum decisions; the editor adds only operator decoding. This prevents a future format change from becoming loadable in the browser but unacceptable to Repair Missing Assets, or the reverse. The complete compatibility matrix passes against Aftrtch_1.syx, including 64-voice concatenation, checksumless and wrapped fixtures, exact voice application, lane isolation, project round-trip, and packaging. The browser-only run measured a 9.43 ms median per voice for 32 selections, retained as an observational result rather than a cross-machine performance guarantee.

Project restoration and missing-asset rescans now classify an existing but unusable cartridge as damaged rather than healthy. The 80s FM browser can still show that no compatible patches were found, while Repair Missing Assets now also offers the actionable repair or clear path. A regression saves and reopens a project referencing an existing junk .syx, verifies the type-8 receipt and unchanged FM algorithm, and clears only the browser metadata. The missing-asset and seven-resource archive gates pass after this change.

Cartridge metadata is now active-asset scoped. If an instrument temporarily switches from 80s FM to another engine, its remembered cartridge path and voice remain available for a later return, but the dormant file is excluded from portable archives and missing-asset warnings. This prevents unrelated missing cartridge alerts and archive bloat without destroying reversible engine work. The regression proves both halves: a dormant missing path is retained but ignored, while an active 80s FM cartridge still completes the seven-resource archive, redaction, remap, and resave sequence.

Cartridge browser metadata now follows instrument-slot ownership during editing. Copying an 80s FM slot carries its cartridge path and selected voice to the destination; copying a different engine clears stale cartridge metadata from the destination; and resetting a private instrument slot clears both fields. The compact slot-level Undo snapshot now includes this metadata, so Undo and Redo restore the exact prior cartridge and voice through both copy and reset operations. The missing-asset lifecycle regression covers all of these transitions, and the complete seven-resource archive round trip continues to pass.

The user-facing instrument Copy/Paste clipboard now applies the same ownership rule as direct lane cloning. An active 80s FM source transfers its cartridge and voice selection, while a non-FM source does not carry dormant FM browser history into the destination. Internal Undo snapshots deliberately retain the exact dormant state so reversibility is not weakened. Focused missing-asset, native instrument-patch lifecycle, and synth/mixer ownership gates pass with this split.

Returning an already-open instrument to 80s FM now explicitly reconstructs its remembered cartridge browser instead of waiting for the synth editor to be closed and reopened. The restoration selects the remembered browser row without applying that voice again, so the current patch parameters are not reset. The compatibility regression switches the same lane to Redshift and back, then verifies all 64 browser entries, voice 64 selection, unchanged algorithm data, and an untouched neighboring lane. The full FM performance/compatibility and native synth-control gates pass.

Undo and Redo now resynchronize an already-open synth drawer after instrument-slot history changes. This includes the visible engine selector, matching engine page, navigation, contextual title, and 80s FM cartridge browser. The FM compatibility test now proves that Undo visibly returns the drawer to Redshift and Redo visibly returns it to 80s FM with voice 64 selected, while both the current and neighboring FM algorithms remain unchanged. The complete FM performance/compatibility gate passes with this live-editor history coverage.