Skip to content

feat: AnaMark .tun microtuning (Load / Reset / host state) - #41

Open
Megagoth1702 wants to merge 4 commits into
midilab:developfrom
Megagoth1702:feature/anamark-tun-support
Open

feat: AnaMark .tun microtuning (Load / Reset / host state)#41
Megagoth1702 wants to merge 4 commits into
midilab:developfrom
Megagoth1702:feature/anamark-tun-support

Conversation

@Megagoth1702

Copy link
Copy Markdown

Summary

Adds AnaMark .tun microtuning support to JC-303:

  • Load a .tun file into a validated 128-note Hz table
  • Reset to 12-TET (from the TUNING / A4 knob)
  • Show the active scale name in the amadeusp GUI while a custom map is loaded
  • Persist custom tuning with host/plugin state (absolute frequencies; original file not required later)
  • Fail closed on invalid / incomplete .tun content, with a line-level error dialog (no silent accept)

UI

Screenshot

Controls sit under the Tuning knob (above the MOD strip) inside the existing 930×363 amadeusp skin (no editor size change).

State What you see
12-TET (default) Load only, centered under the Tuning knob
Custom scale loaded [Reset] left · [Load] center · scale name right

While a custom map is active:

  • Reset and the scale name become visible
  • The Tuning knob is disabled and dimmed until Reset (A4 fine-tune applies to 12-TET only)
  • Failed loads keep the previous map and show “Tuning load failed” with filename + specific error (often including line number)

Behaviour / formats

Supported: AnaMark .tun v2 only (no Scala .scl / .kbm, no MTS-ESP).

Section Policy
[Exact Tuning] present Authoritative. Requires valid BaseFreq + all 128 Note n = <cents>. Non-numeric / incomplete → error. No fallback to Functional if Exact is present but broken.
Functional only (no Exact) InitEqual = (refNote, refHz) seeds 12-TET; optional Note T = "#=B %C" overrides in file order (base chaining). Malformed lines → error, not silent skip.

Conversion: cents → Hz via f = BaseFreq * 2^(cents/1200) (Exact) or chained Functional bases.

Technical notes

  • No new third-party deps. In-house pure C++ loader: src/dsp/tuning/ (TuningTable, TuningFileLoader, smoke test).
  • Single pitch path in Open303: note-on / slide / release always use noteToHz() against a double-buffered 128-entry bank (installPitchMap). ET is just another full map.
  • Ownership: JC303 owns custom vs ET policy, host XML, UI notifications; Open303 stays policy-free (master A4 + banks only).
  • TUNING knob: always updates master A4; rebuilds live 12-TET only when custom is inactive; does not rewrite custom absolute frequencies.
  • Host state: APVTS child CustomTuning (active, name, freqs CSV of 128 Hz, optional path). Malformed payloads fall back to clean 12-TET.
  • LFO (from develop): destinations filter / volume / pitch still apply in getSample. Pitch LFO is a frequency multiplier on oscFreq after table lookup + slew — works with custom maps and 12-TET.
  • Optional CMake overrides keep stock defaults for upstream (JC303 / J303, full format set); private builds may pass -DJC303_PRODUCT_NAME=…, -DJC303_PLUGIN_CODE=…, -DPLUGIN_FORMATS=….
  • amadeusp only — midilab theme has no Load/Reset bar yet.

Testing

  • VST3 Release build on Windows (VS / CMake) after merge with developBUILD OK
  • Load valid AnaMark .tun → scale name + audible pitch change
  • Reset → 12-TET, Tuning knob re-enabled
  • Host state save/recall of custom map
  • Invalid files rejected with dialog (manual: deleted Exact lines, letters where numbers belong — previously silently accepted)
  • Standalone smoke test: src/dsp/tuning/tuning_smoke_test.cpp
    Covers Exact complete/incomplete/non-numeric, bad BaseFreq/InitEqual, no Exact→Functional fallback, Functional init/override/chain/garbage, CSV round-trip, validation — all passed after develop merge

Out of scope / known limits

  • midilab GUI theme: no Load/Reset bar (amadeusp only for v1)
  • No Scala .scl / .kbm
  • No MTS-ESP
  • No in-plugin scale editor / skin redesign
  • LFO UI on amadeusp/midilab is whatever develop already ships (this PR does not add LFO widgets)

Merge note for maintainers

  • Branch includes current develop (merge commit 0597f0c). Merging this PR into develop should be conflict-free at tip 8e16c3e.
  • Integration: CMake lists both dfl_LFO.cpp and TuningFileLoader.cpp; Open303 includes both dfl_LFO.h and TuningTable.h.

Load .tun files into a double-buffered pitch map (A4 / master tune
policy), wire Open303 note frequencies from the active table, and add
TuningFileControl under the Tuning knob (Load, Reset, scale name, dim
when custom). Includes smoke test, README, and build ignore hygiene.
Fail closed on malformed Exact/Functional entries instead of silently
skipping bad values or falling back to Functional when Exact is present
but broken. Surface specific messages (line number + cause) in the load
failure dialog, and extend smoke tests for non-numeric notes, bad
BaseFreq/InitEqual, incomplete Exact, and no Exact-to-Functional fallback.
Prepare the AnaMark TUN feature for a clean PR onto midilab/jc303 develop.
Resolve trivial dual-ownership conflicts by keeping both LFO (dfl_LFO) and
tuning (TuningFileLoader / TuningTable) sources and includes. Pitch LFO
remains a multiplier on oscFreq after noteToHz table lookup.
@dfl

dfl commented Sep 1, 2026

Copy link
Copy Markdown

noice! 🤘

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants