@maccesar/titools 5.2.3 → 5.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +96 -87
  2. package/docs/examples.md +10 -3
  3. package/package.json +2 -2
  4. package/skills/ti-synthengine/SKILL.md +50 -18
  5. package/skills/ti-synthengine/assets/example/README.md +19 -6
  6. package/skills/ti-synthengine/assets/example/app.js +69 -12
  7. package/skills/ti-synthengine/assets/index.d.ts +106 -0
  8. package/skills/ti-synthengine/references/advanced-examples.md +34 -9
  9. package/skills/ti-synthengine/references/api.md +327 -246
  10. package/skills/ti-synthengine/references/audio-engine.md +28 -14
  11. package/skills/ti-synthengine/references/audio-pipeline-validation.md +1 -1
  12. package/skills/ti-synthengine/references/examples.md +5 -5
  13. package/skills/ti-synthengine/references/guide.md +40 -21
  14. package/skills/ti-synthengine/references/instruments-and-effects.md +164 -0
  15. package/skills/ti-synthengine/references/native-tone-repetition-validation.md +59 -0
  16. package/skills/ti-synthengine/references/native-variation-proposal.md +182 -0
  17. package/skills/ti-synthengine/references/native-variation-validation.md +63 -0
  18. package/skills/ti-synthengine/references/native-variation.md +285 -0
  19. package/skills/ti-synthengine/references/recipes-interaction.md +3 -1
  20. package/skills/ti-synthengine/references/recipes.md +3 -2
  21. package/skills/ti-synthengine/references/recommendations.md +5 -5
  22. package/skills/ti-synthengine/references/sound-design.md +18 -16
  23. package/skills/ti-synthengine/references/source-notes.md +54 -37
  24. package/skills/ti-synthengine/references/source-snapshot.json +188 -144
  25. package/skills/ti-synthengine/references/stats-performance.md +1 -1
  26. package/skills/ti-synthengine/references/troubleshooting.md +43 -35
@@ -0,0 +1,182 @@
1
+ > Source snapshot: official TiSynthEngine documentation at commit `ec04f4e634075c41b55f66279eabcd78b9652d58` (2026-11-02). Published module 1.1.0; public API 1.1.0. Upstream `documentation/native-variation-proposal.md` SHA-256: `17b74434b0e03e5353b71c693d4ec722dede92fee8aed142003e67f60e158e27`.
2
+
3
+ <a id="native-per-attack-variation--proposal-and-implementation-plan"></a>
4
+
5
+ # Native per-attack variation: proposal and implementation plan
6
+
7
+ Historical proposal, written on November 1, 2026 before implementation. The design and unchecked plan below record that starting point, not outstanding implementation work. Current source and local 1.1.0 candidates implement the feature; use the [implemented contract](native-variation.md) and [technical verification](native-variation-validation.md) for current behavior and acceptance. Writing the original proposal did not change native sources, binaries or packages.
8
+
9
+ Implementation update (November 1, 2026): current source and local 1.1.0 candidates implement the contract on both platforms. See [the implemented contract](native-variation.md) and [verification](native-variation-validation.md). The proposal and historical feasibility findings below describe the starting point; they are not release announcements.
10
+
11
+ ## Purpose and ownership
12
+
13
+ Generate subtly different versions of the **same** sound: an anvil struck at different places, not a different object on each hit. The Engine owns random selection once per new native voice. Apps supply an authored recipe, permitted ranges and amount. Studio exposes the controls and saves those choices; Frontier and Guardian use the same public property.
14
+
15
+ The existing oscillator, anti-aliasing, ADSR, fixed event gain, chord compensation, routing, output protection and Oboe/Apple audio backends remain intact. This is an optional addition at voice creation, not a redesign of mixing or dynamics. A recipe without variation must preserve its existing output exactly. Do not revive the rejected Studio touch adapters, gesture delays, normalization or output-protector experiments.
16
+
17
+ Studio's independent catalog expansion imports Playground, Frontier and Guardian sound examples. That work can ship independently. It does not implement random selection or require this future feature. The accepted pure-sine preset formerly named Cristal is now Seno puro (`clean`); preserve its approved sound.
18
+
19
+ ## Proposed public contract
20
+
21
+ Use one optional dictionary named `variation` on `playTone`, `playChord`, `noteOn`, sequence `voice`, and step `overrides`. Unknown fields remain rejected by the closed contracts.
22
+
23
+ ```javascript
24
+ synth.playTone({
25
+ frequency: 620,
26
+ waveType: WAVE.SINE,
27
+ duration: 180,
28
+ attack: 1, decay: 65, sustain: 0.12, release: 110,
29
+ fmRatio: 2.7, fmDepth: 3.2, fmDepthEnd: 0.4,
30
+ volume: 0.30,
31
+ variation: {
32
+ amount: 0.5,
33
+ // seed: 20261101, // Optional: deterministic comparison/testing.
34
+ ranges: {
35
+ volume: [0.291, 0.30],
36
+ fmDepth: [2.944, 3.456],
37
+ pitchCents: [-12, 12]
38
+ }
39
+ }
40
+ });
41
+ ```
42
+
43
+ - Omitted `variation`: current behavior, no extra RNG calls or changed noise state.
44
+ - `amount`: finite number 0 to 1, default 1 when variation is supplied. Zero bypasses sampling and leaves authored values unchanged. The dictionary is still validated.
45
+ - `ranges`: dictionary of permitted field ranges. Empty ranges have no audible effect. Each value is exactly two finite numbers `[minimum, maximum]` with minimum ≤ maximum. Equal endpoints deliberately fix a value when amount is 1.
46
+ - `seed`: optional integer 0 to 4294967295. Zero is a valid deterministic seed, not the sentinel for automatic seeding. Omission uses the Engine's independent variation stream.
47
+ - Non-pitch ranges are **absolute native parameter values**, not multipliers. Interpolate from the authored value toward the sampled value: `authored + amount * (sampled - authored)`.
48
+ - `pitchCents` is a relative offset, initially zero. Multiply the already tuned/detuned frequency and its active sweep endpoint by `2 ** (amount * sampledCents / 1200)`.
49
+ - Each range samples a uniform distribution. No quantized three-variant selection, hidden rounding to preset choices or automatic rejection of a natural repeated value.
50
+
51
+ ### Version-one fields
52
+
53
+ | Range field | Bounds and compatibility | Identity rule |
54
+ | ------------ | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
55
+ | `volume` | Current native event volume 0 to 2 | Sample before existing fixed chord compensation; no other voice is rescaled. |
56
+ | `filterHz` | 20 to active frequency ceiling; an enabled resonant filter is required | Changes starting cutoff; the authored `filterHzEnd` remains the same endpoint. |
57
+ | `filterQ` | 0.5 to 10; enabled resonant filter required | Does not change filter type. |
58
+ | `fmDepth` | 0 to 10; SINE only | Does not change `fmRatio`; an authored FM endpoint, including zero, remains unchanged. |
59
+ | `noiseRate` | Positive colored-noise range accepted by current API; NOISE only | Does not change waveform; authored noise sweep endpoint stays fixed. |
60
+ | `lowPassHz` | Enabled cutoff, 20 to active ceiling | Does not randomly switch the low-pass on/off. A disabled low-pass cannot receive this range. |
61
+ | `pulseWidth` | 0.05 to 0.95; SQUARE only | Does not change waveform; authored PWM endpoint stays fixed. |
62
+ | `pitchCents` | −1200…1200, intentional opt-in | Preserve both the starting pitch and pitch-sweep trajectory under the same multiplier. |
63
+
64
+ Native validators must use the actual existing source/range rules rather than assuming all numeric intervals are continuous. In particular, do not interpolate through an invalid bypass/cutoff gap. Pitch corners must remain within the active frequency ceiling, including an existing detune and every frequency in a chord/step. Reject a dictionary whose reachable extremes are invalid; never silently clamp the variation to conceal an invalid recipe.
65
+
66
+ Version one deliberately keeps waveform, wavetable, quantization, FM ratio, filter type, ADSR, event duration, rhythmic interval, routing and priority authored. This preserves the principal sound shape and simplifies timed-envelope correctness. Optional duration/ADSR variation is a separate future design: it must not randomly violate envelope budgets or move a sequence's rhythmic grid. The earlier discussion of varying 140 to 170 ms was an illustration, not a shipped promise.
67
+
68
+ ### Attacks, held notes, chords and sequences
69
+
70
+ 1. **Timed tone / held note:** sample once as the new voice starts. Reuse the sampled values throughout sustain and release. Playing another note, lifting another finger or repainting UI must not resample this voice.
71
+ 2. **Chord:** validate the complete call before admission. Each admitted voice gets its own deterministic draws, then the existing compensation applies. Keep atomic chord admission and existing pan/detune behavior.
72
+ 3. **Sequence:** sample when each scheduled voice actually starts in the native transport, not when JavaScript submits the score. Every new iteration gets new draws. Rests consume none. This is the main advantage over baking variations in JS step overrides.
73
+ 4. **Inheritance:** a step without `variation` inherits the prepared root voice. A step with `variation` replaces that whole dictionary. `{amount:0}` disables it for that step; partial nested merge semantics are intentionally avoided.
74
+ 5. **Updates:** `updateVoice` does not accept this attack-only dictionary. Explicit live edits retain their normal smoothing and set the requested fields without resampling other fields or restarting the envelope. `updateSequence` may change ranges/amount for future attacks; active voices keep their sampled state.
75
+ 6. **Seed stream:** a seeded tone/note call begins at attack index zero, so repeating exactly that call reproduces it. A seeded chord uses note ordinal as a further index. A seeded sequence owns an attack counter that continues across loops and normal revisions. Stop/new play restarts the seeded stream; changing the seed restarts it when the new revision applies. Re-publishing the same seed must not accidentally reset it.
76
+
77
+ ## Deterministic and realtime design
78
+
79
+ Implement one small C-compatible shared helper, provisionally `shared/synth_variation.h`, used by both platform renderers. Use an integer-defined generator/hash with golden vectors, stable field slots and explicitly defined conversion to a uniform scalar. A counter-based `(seed, attack index, chord voice ordinal, field slot)` scheme avoids dependence on JavaScript property order and on adding another optional field. Review statistical quality against this sound-design use; this is not a cryptographic RNG.
80
+
81
+ Maintain a separate random stream/counter from oscillator noise. Enabling variation in another voice must not change an existing noise waveform. Omitted/zero amount consumes no variation draws and does not disturb legacy noise state. Seed automatic streams at control/start time, never by calling an OS entropy service in the audio callback.
82
+
83
+ Parse/validate dictionaries and prepare numeric fixed-size configuration on the producer/control side. At an attack, native code uses bounded constant-time arithmetic and preallocated storage. No allocation, locks, sleeps, collection creation, JSON, JS calls, OS RNG or logging in the callback. No per-sample randomization of these parameters. Existing LFO and sweep processing continues normally after the attack.
84
+
85
+ Inspect actual packet and bank sizes before choosing storage. `SynthSequenceVoice` is copied into up to 512 steps, three prepared banks and four transports; adding a dictionary-sized object or copying large tables for variation would be wasteful. Use a bit mask plus fixed numeric fields, and measure the resulting worst-case memory instead of guessing.
86
+
87
+ ## Verified implementation seams to inspect
88
+
89
+ - `android/src/ti/synthengine/SynthContract.java`: closed timed/held/chord validation, including `validateNoteOn`.
90
+ - `android/src/ti/synthengine/SynthStudioContract.java`: sequence voice/override validation and prepared packet encoding.
91
+ - `android/src/ti/synthengine/SynthThread.java` and `TiSynthEngineModule.java`: command preparation, bridge/default metadata.
92
+ - `android/jni/SynthOboeEngine.cpp`: `Voice`, command-to-voice setup, sequence attack creation and JNI packet decoding.
93
+ - `ios/Classes/TiSynthengineContract.{h,m}`: equivalent validation and prepared sequence/command structures.
94
+ - `ios/Classes/TiSynthengineModule.m`: `startVoiceFromCommand`, sequence-created voices and defaults/capabilities.
95
+ - `shared/synth_sequence.h`: `SynthSequenceVoice`, `SynthSequenceStep`, prepared banks and attack events.
96
+ - `shared/synth_held.h`, `synth_modulation.h`, `synth_core.h`: preserve held envelope, modulation and oscillator contracts.
97
+ - Existing `tests/engine_render_host.cpp`, platform contract regression tests and actual Apple callback host tooling: extend their relevant cases rather than inventing a second renderer.
98
+
99
+ The graph was used to identify these seams. It reports partial indexing of `synth_sequence.h`; exact source is authoritative. Confirm concrete entry-point signatures against the current checkout before editing.
100
+
101
+ ## Range authoring policy
102
+
103
+ A preset should define a small subset of the eight fields, with most fields left alone. Musical instruments preserve tuning by default. Mechanical/game impacts may opt into small pitch changes. Amount 100% means the full **authored subtle range**, not an uncontrolled maximum across the API.
104
+
105
+ Starting references from the archived Frontier experiment:
106
+
107
+ - Impacts/destruction: pitch multiplier 0.95 to 1.05, about −88.8…84.5 cents. Shots: 0.97 to 1.03, about −52.7…51.2 cents. These are optional FX bounds, not instrument defaults.
108
+ - Colored-noise rate/cutoff: 88 to 112% of the anchor; FM depth 92 to 108%; Q 95 to 105%.
109
+ - Event level: 97 to 100% of anchor, deliberately never louder than that anchor.
110
+ - ADSR/timing/voice count remain unchanged.
111
+
112
+ Convert these references into absolute native ranges in each recipe. Do not bake those percentages as mandatory Engine defaults: the Engine must not guess what an anvil, glass or trumpet means. Technical range validity and recognizability are distinct; audition the material at low, midpoint and full amount.
113
+
114
+ <a id="feasibility-and-compatibility-review--november-1-2026"></a>
115
+
116
+ ## Feasibility and compatibility review: November 1, 2026
117
+
118
+ The proposed opt-in feature is feasible. It requires changes to both validators, bridge packets, command preparation and the voice-start paths, including sequence attacks. No implementation or performance acceptance is claimed by this review.
119
+
120
+ ### Compatibility requirements and consumer impact
121
+
122
+ | Consumer scenario | Required behavior |
123
+ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
124
+ | An installed app keeps its existing module package | Nothing changes; the module is bundled with that app. |
125
+ | An app rebuilds with the new package but never supplies `variation` | Existing recipe parameters, admission, envelopes, gains, processing and noise algorithm/state progression remain unchanged. No variation draws occur. |
126
+ | An app supplies valid `{amount:0}` | Validate the dictionary, then take the same rendering path as the unvaried recipe. |
127
+ | An app explicitly supplies active ranges | Only new attacks sample the permitted fields. Existing sustained voices are not resampled. |
128
+ | A newer app runs with an older package | Omit the entire `variation` key unless feature support is advertised. Old closed validators reject unknown keys, including an amount-zero dictionary. Do not retry an unsupported recipe indefinitely. |
129
+
130
+ Package version and reported API compatibility are separate concerns. Playground's `Resources/lib/synthSafe.js` requires `apiVersion === '1.0.0'`; Guardian's audio system also compares its expected API version exactly. Changing that string would make those apps reject the module or fall back even if every existing sound call remained valid. Studio's inspected source does not impose that exact-version check; Frontier's inspected app/Resources source contains no `apiVersion` check.
131
+
132
+ **Recommendation, not a published contract:** retain the existing compatibility identifier for unchanged core methods and advertise this optional extension explicitly on both platforms, for example `capabilities.variation: true` plus `variationSchemaVersion: 1`. Publish a new module package version when implementation is ready; do not replace the 1.0.2 release. If the API identifier instead changes, prepare the affected consumer checks before claiming drop-in compatibility. Current iOS metadata omits Android's capability map, so adding the variation flag must be implemented on iOS too.
133
+
134
+ Apps that do not opt in need no recipe edits. Opting in requires the new package and a support check. Studio additionally needs saved range/amount fields, schema migration, validation, serialization to every supported playback path and explicit controls. Frontier/Guardian need material-specific ranges attached to their recipes; visual variant selection remains a separate concern. No shared recipe registry or reusable sound-object API is required.
135
+
136
+ ### Native details that the implementation must preserve
137
+
138
+ 1. **Validate the effective step voice.** Root voice and step overrides already merge before validation. Validate inherited ranges against each effective waveform/filter and every effective pitch. A step that changes from sine to noise cannot silently discard inherited FM variation: replace the variation dictionary or explicitly disable it for that step. A variation-only `updateSequence` edit must not reset its seed counter or touch active voices.
139
+ 2. **Apply samples before all derived state.** Android held starts initialize `SynthLive` and may inherit compatible release-tail phase/filter state. Sequence sweeps also read a retained `sequenceShape`. Sample before building those derived values, and retain the sampled starting shape and pitch-scaled endpoint. Sampling only the initial volume/filter/frequency would allow the next live/sweep update to overwrite it. Apply the equivalent rule on iOS.
140
+ 3. **Define admission and draw order.** Use a fixed-width attack counter and stable field slots. Advance an event counter only after that attack is admitted; rejected events and rests consume no draws. All notes in an admitted chord share that event index and use their note ordinal for independent draws. Automatic streams must distinguish successive one-shot calls. Explicitly seeded one-shot calls restart at index zero, so repeating the same seeded call intentionally repeats its sampled parameters. Specify counter wrap in the shared helper.
141
+ 4. **Do not confuse parameter determinism with noise PCM.** An explicit variation seed reproduces selected parameter values. Existing oscillator noise is separate: Android currently seeds a voice from its address/sample index; iOS uses time/slot state. A variation seed does not promise identical noise samples, complete PCM or cross-platform output. Compare absent/zero variation against the old renderer using the same controlled noise initialization in the test harness; preserve production noise behavior. Do not change the noise generator to make this test pass.
142
+ 5. **Preserve waveform meaning.** Pitch variation is useful for tuned sources; ordinary oscillator pitch does not transpose a NOISE source. Author noise effects with clock/filter variation. Keep waveform, ADSR, duration, timing, routing, priority and FM ratio outside V1, as already specified. In particular, authoring FM ranges from an initially zero depth must still satisfy the effective FM-ratio/source rules.
143
+ 6. **Measure storage and callback cost.** A 64-bit macOS host `sizeof` probe of the unchanged header measured `SynthSequenceVoice=824`, `SynthSequenceStep=1400`, one config `718,920` and one triple-bank transport `2,156,928` bytes. Four such transports total `8,627,712` bytes, excluding renderer voices, command queues and other state. A naive candidate containing amount, sixteen double endpoints, mask and seed flags is 152 bytes; adding it to every step of every bank of four transports would add 933,888 bytes before other copies. These are baseline/layout measurements, not an implemented storage design or a device benchmark. Prefer a compact representation and consider sharing repeated effective configurations within an immutable prepared bank if measurement justifies it.
144
+
145
+ ### Acceptance before consumer rollout
146
+
147
+ The compatibility claim is a release gate, not a conclusion established by this proposal. Tests must cover omitted and amount-zero variation through timed tones, chords, held notes and native sequences on both platforms, including existing profiles, release overlap and deterministic noise fixtures. Assert unchanged authored configs, stable sustained voices, no extra noise-state consumption, atomic chord admission, unchanged gain compensation, correct sweep starts/endpoints and newly sampled loop attacks. Measure dense publication memory and callback load; retain physical listening as a separate acceptance step.
148
+
149
+ The review ran only source inspection and the isolated host layout probe. No renderer, bridge, package, existing app sound or physical device was changed.
150
+
151
+ ## Implementation plan and gates
152
+
153
+ - [ ] **Contract first:** add paired Android/iOS rejection/acceptance tests for omitted, zero, minimum/midpoint/maximum amount, unknown keys, malformed/inverted/nonfinite ranges, incompatible sources, bypass gaps, invalid seeds and pitch corners. Watch new-feature tests fail before adding support.
154
+ - [ ] **Shared sampling helper:** implement fixed-size config and deterministic math; test golden vectors, field ordering, seed zero, repeatability, independence from noise RNG, bounded outputs and no mutation of authored configurations.
155
+ - [ ] **Both renderers in one work cycle:** add strict bridge/prepared fields, initialize them on every path and sample at actual voice creation, including native sequence retriggers. Review timed/chord/held/sequence paths separately. No platform-only completion.
156
+ - [ ] **Native behavior tests:** compare absent/zero-variation PCM to the prior renderer for every waveform/profile/path with controlled identical noise initialization in the harness; use representative fixed seeds to compare platform selections and rendered outcomes. Confirm held notes stay stable when unrelated voices start/release; confirm eight/32-note chords and looping sequence attacks advance the stream correctly.
157
+ - [ ] **Lifecycle and edits:** stop/panic/restart, failed admission, voice stealing, capacity saturation, stale sequence revisions, seed changes, 512 steps/four transports, varying override source types, updates while a note sustains. Verify no partially admitted chord and no stale uninitialized variation mask.
158
+ - [ ] **Performance/memory:** measure callback work for 1/8/16/32 voices and four active sequences, max bank memory and publication allocation behavior. Absent/disabled paths must remain effectively unchanged. Never reinterpret a host benchmark as physical latency/listening acceptance.
159
+ - [ ] **Metadata/docs/example:** advertise support on both platforms in `getDefaults()` capabilities/limits, document fields/defaults/seed/loop behavior and add a clear example. Review exact API-version checks in consumer apps before selecting the feature's development/API version. Do not silently publish 1.0.2 binaries with an undocumented new contract.
160
+ - [ ] **Consumer integration:** once the native packages exist, Studio forwards the property from its switch/amount/preset bounds; Frontier and Guardian supply their ranges. Remove app-side samplers, preserve visual variant selection, stable IDs and saved edits. The catalog remains usable when the optional native property is absent.
161
+ - [ ] **Physical acceptance:** same-anvil identity, non-repeating nearby strikes, tuned instrument chords, stable held notes, complete finite effects, fresh sequence-loop variations and representative headphones/speakers on Android/iOS. Record listening separately from validator/PCM evidence.
162
+
163
+ Do not broaden to the entire module suite automatically. Start with the new shared helper and affected contract/render tests. Broaden when a shared struct/packet change makes existing serialization/lifecycle regressions relevant, and explain that concrete reason. Run release-required checks only if an actual release is requested.
164
+
165
+ ## References and workspace state at proposal time
166
+
167
+ - Current base: TiSynthEngine 1.0.2 packages, public API 1.0.0. Engine checkout was `main` ahead of `origin/main` by one preexisting documentation commit before this proposal; do not discard it.
168
+ - Engine mixing/output/oscillator fixes are valued by César. He initially froze the Engine during the unrelated Cristal investigation, then authorized this specific optional variation feature and requested the proposal in the Engine repository.
169
+ - Native implementation is **not started** by this document. No release, tag, force push, consumer installation or physical-device operation is authorized merely by the plan.
170
+ - Complete Studio JS prototype (archived, removed from production): `/Users/cesar/Developer/validation-evidence/TiSynthEngine/native-variation-proposal-2026-11-01/studio-prototype/README.md`.
171
+ - Complete Frontier JS prototype/range tests (archived, removed from production): `/Users/cesar/Developer/validation-evidence/TiSynthEngine/native-variation-proposal-2026-11-01/frontier-prototype/README.md`.
172
+ - Frontier prototype proved 271 zero-amount recipe results identical to HEAD and exercised 162 generator/action cases ×32 seeds. Studio prototype checked per-attack ownership and sampled-sequence limitations. These are reference experiments, **not native implementation evidence**. They sampled some fields differently from the proposed native V1 contract; do not copy them wholesale.
173
+ - Studio ongoing catalog: `/Users/cesar/Developer/Apps/TiSynth-Studio/documentation/sound-library.json`; generator `tools/import-sound-examples.js`. Source files are snapshotted at development time; the shipped app must not require sibling source projects.
174
+
175
+ ### Historical implementation request
176
+
177
+ > Read AGENTS.md, docs/project/status.md and documentation/native-variation-proposal.md. Implement the optional native per-attack variation contract on Android and iOS, preserving absent/disabled behavior and existing DSP/mixing. Start with the scoped tests and verify the sequence-loop/held-note/seed guarantees. Treat the document as a proposal whose contradictions should be resolved explicitly; do not publish a release or install on devices without the corresponding instruction. Integrate consumer controls after the native contract and packages are verified.
178
+
179
+
180
+ ## Deferred API-model question (separate scope)
181
+
182
+ César raised whether a recipe should be a registered reusable sound object with a method such as `clickSeco.play()`, instead of a reusable options dictionary submitted on every call. He explicitly asked to defer this observation and continue the current catalog/proposal work. Record it for a later API-design discussion; it is not authorization to add a sound registry, cache or another playback wrapper to this feature. Do not expand native variation into a preset-object lifecycle project.
@@ -0,0 +1,63 @@
1
+ > Source snapshot: official TiSynthEngine documentation at commit `ec04f4e634075c41b55f66279eabcd78b9652d58` (2026-11-02). Published module 1.1.0; public API 1.1.0. Upstream `documentation/native-variation-validation.md` SHA-256: `df18826824984cb440d7412b547663ba6b8a7152bc347508114d7af982c90e68`.
2
+
3
+ # Native variation: technical verification
4
+
5
+ Initial host and build verification on November 1, 2026, against Engine base `5a328ad`. Native sources build local 1.1.0 Android/iOS candidates; public API remains 1.0.0. The measurements below describe that technical stage. Later consumer installation and listening observations are recorded separately in the [follow-up](#consumer-installation-and-listening-follow-up); they do not turn the host measurements into physical-device benchmarks. No 1.1.0 release is announced by this report.
6
+
7
+ ## Contract and rendering
8
+
9
+ - Shared C helper: a known SplitMix64 vector, independent attack/ordinal/field coordinates, bounded sampling, repeatability, no mutation of authored configuration, amount-zero bypass and schema-slot independence.
10
+ - The paired corpus plus nonfinite probes exercises 156 cases on each production validator: tones, chords, held starts, sequence voices/overrides, source restrictions, malformed/inverted ranges, booleans, seeds including zero/uint32 maximum, amount endpoints, tuning/detune/chord/sweep pitch corners and zero-depth FM starts. `updateVoice` rejects variation.
11
+ - Actual Android renderer and extracted actual Apple callback hosts cover deterministic parameters, held stability, independent chord ordinals, loop attacks, rests, rejected admission, revision continuity and changed seeds. A focused shared regression reproduced the structural-edit counter reset before its fix, then verified inherited-rest insertion, explicit root-seed change, appended explicit step seed zero and reordered declarations.
12
+ - Absent/amount-zero PCM matches within the new renderer across six waveforms, four mixing profiles, held/chord/sequence paths and release. Noise initialization is controlled by existing host fixtures; production noise generation is unchanged.
13
+ - Android's pre-feature and candidate `engine_render` fixture output, including its PCM hashes, matches exactly.
14
+ - Apple pre-feature versus candidate PCM is byte-identical across the original 120 lifecycle/effects/transport scenarios: 29,692,526 bytes. The comparator's `--pcm-only` mode excludes internal structure snapshots because prepared sequence layouts intentionally changed.
15
+ - An additional Apple compatibility driver compares the pre-feature renderer against both candidate absent and amount-zero modes across six waveforms, four profiles and four paths (timed, held, chord, native sequence), including overlap/release: 2,359,296 bytes match exactly in each comparison.
16
+ - Existing native renderer, sequence-publication, retrigger, bypass, startup, contract and canonical-state memory checks accompany the focused variation cases. Existing tests with handcrafted packets now use the new internal header/step widths; the public score format is unchanged.
17
+
18
+ Commands: `python3 tests/run_variation_contracts.py`, the two `variation_*_regression.c` executables, `python3 tests/run_native_hosts.py variation_engine`, and `python3 tools/build_ios_render_host.py --output /tmp/variation-ios --driver tests/variation_render_ios.m`. The compatibility driver is `tests/variation_compatibility_ios.m`, built once against the Git base extracted to an external temporary directory and once against the candidate. No checkout/HEAD changes are needed.
19
+
20
+ ## Storage and callback work
21
+
22
+ 64-bit macOS layouts:
23
+
24
+ | Structure | Base bytes | Candidate bytes |
25
+ | ---------------------- | ---------: | --------------: |
26
+ | Sequence voice | 824 | 968 |
27
+ | Sequence step | 1,400 | 1,544 |
28
+ | Prepared configuration | 718,920 | 794,704 |
29
+ | Triple-bank transport | 2,156,928 | 2,384,288 |
30
+ | Four transports | 8,627,712 | 9,537,152 |
31
+
32
+ The four-bank-owner total grows by 909,440 bytes, including prepared explicit-seed identities. This excludes renderer voices/tails, command queues, effect rings, bridge arrays and canonical Java/Objective-C options. A variation configuration itself is 144 bytes. No dictionary or variable-size object reaches the callback. Explicit step seed declarations are sorted during preparation; application compares prepared root identity and seed declarations without sorting or allocation.
33
+
34
+ The callback measurements below run four native transports at 48 kHz, 256 frames/block, 1,000 warmup and 1,000 measured blocks. Voice counts are distributed over the four transports; unused transports contain rests. Full-amount three-field variation is selected per admitted attack. Measurements are thread CPU time divided by rendered block budget, on the macOS hosts, without opening audio hardware.
35
+
36
+ | Target voices | Android variation off | Android variation on | Apple variation off | Apple variation on |
37
+ | ------------- | --------------------: | -------------------: | ------------------: | -----------------: |
38
+ | 1 | 0.49% | 0.41% | 0.48% | 0.50% |
39
+ | 8 | 1.43% | 1.41% | 1.85% | 1.78% |
40
+ | 16 | 3.01% | 2.59% | 3.07% | 3.08% |
41
+ | 32 | 6.48% | 4.80% | 5.53% | 6.04% |
42
+
43
+ Run-to-run/cache/scheduling variation is visible; these single samples do not estimate a causal speedup or physical overhead bound. Other app builds were running during parts of this session. Wall time is retained in the external logs, separately from thread CPU time. At 32 voices, enabled mean CPU work is approximately 0.256 ms/Android callback and 0.322 ms/Apple callback on this host, with a 5.333 ms block budget.
44
+
45
+ A 512-step publication check (100 edits, 32 pitches/step) measured Android JNI/native producer CPU p95 0.335 ms and Apple Foundation producer CPU p95 10.609 ms. These execute outside the audio callback and exclude Android Java preparation. The canonical Java memory regression verifies detached input, immutable reuse, rejected-edit isolation and release of accepted bridge packets. None of these results represents phone memory pressure, thermal stability, underruns or latency.
46
+
47
+ ## Packages and consumer acceptance
48
+
49
+ Both module builds passed with `ti build -p android/ios --build-only`. Native-build archive SHA-256 before the final documentation-only refresh (the external packaging proof records the refreshed archives and proves native binary members unchanged):
50
+
51
+ - Android: `4fc5cb31ecb16a0d3d8acc9599f87f71e322778f92073fa0042e49b24fcfbacb`.
52
+ - iOS: `71b28d2473a7b56d7e966addad636037b985c1eb5bffa9f8a2e0737efe3ce2b7`.
53
+
54
+ Consumer source tests, installed candidate-file parity and platform build evidence are recorded in each app's validation/session notes. The official 1.0.2 directories/archives are retained. Capability-gated fallback and authored range gains are tested independently from listening. Studio's personal presets, A/B, disabled/zero paths, layer ownership and pattern restoration have their own tests; module validation does not replace those checks.
55
+
56
+ Technical hosts establish parameter/transport/rendering behavior within their stated scope. Physical real-time acceptance and comparative listening at low/mid/full amount remain separate and require free authorized devices.
57
+
58
+
59
+ ## Consumer installation and listening follow-up
60
+
61
+ The current Studio, Frontier and Guardian builds using module 1.1.0 were installed over the existing apps on the OPPO CPH2639 and physical iPad (9th generation), preserving app data. Signed physical iOS builds succeeded with the projects' selected SDKs; these device builds do not require the missing simulator runtime.
62
+
63
+ César reported that Studio's variations and control edits sound good, and specifically accepted Frontier's Órbita Neón / Roca Frágil variation. The device and output route were not specified in that feedback. This records those observations without extending them to all effects, both platforms or sustained maximum-load performance.
@@ -0,0 +1,285 @@
1
+ > Source snapshot: official TiSynthEngine documentation at commit `ec04f4e634075c41b55f66279eabcd78b9652d58` (2026-11-02). Published module 1.1.0; public API 1.1.0. Upstream `documentation/native-variation.md` SHA-256: `650d2c675dc4b4e09a85521812b7fedd3b02cb993a451378a8082e2b7782303a`.
2
+
3
+ # Native per-attack variation
4
+
5
+ `variation` lets the engine choose bounded parameter values when it admits an attack. Repeated shots can have different pitch, noise color or filter settings without JavaScript randomization. Each voice keeps the chosen values for its lifetime; existing envelopes, sweeps and LFOs continue from that starting configuration.
6
+
7
+ Android and iOS support this dictionary in the 1.1.0 packages, with public API version `1.1.0` and variation schema version `1`. The published 1.0.2 packages do not accept it.
8
+
9
+ ## Select version 1.1.0
10
+
11
+ Install the matching Android and iOS 1.1.0 archives in the app and select them in `tiapp.xml`:
12
+
13
+ ```xml
14
+ <modules>
15
+ <module platform="android" version="1.1.0">ti.synthengine</module>
16
+ <module platform="iphone" version="1.1.0">ti.synthengine</module>
17
+ </modules>
18
+ ```
19
+
20
+ Rebuild and reinstall the application after replacing native modules. Copying module files alone does not update an installed app. Keep the published 1.0.2 archives separate; version 1.1.0 reports API `1.1.0`.
21
+
22
+ ## Detect support and start playback
23
+
24
+ Check both metadata fields. API version alone does not identify variation support. Omit the property completely when either announcement is missing, including when the requested amount is zero.
25
+
26
+ ```javascript
27
+ var synth = require('ti.synthengine')
28
+ var contract = synth.getDefaults()
29
+ var supportsVariation = !!(contract.capabilities
30
+ && contract.capabilities.variation === true
31
+ && contract.variationSchemaVersion === 1)
32
+ var W = contract.waveTypes
33
+
34
+ synth.start({ maxVoices: 8, maxSequences: 1 })
35
+ synth.setVolume({ volume: 0.6 })
36
+ ```
37
+
38
+ The examples below use those variables. Handle rejected publications through the normal [return values and diagnostics](api.md#return-values). For rapid effects, size the shared voice pool for overlapping durations and releases.
39
+
40
+ ## Supported locations
41
+
42
+ | Call or score field | Behavior |
43
+ | -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
44
+ | `playTone({ variation, ... })` | Chooses once for an admitted timed tone |
45
+ | `repeatTone({ variation, intervalMs, ... })` | Retains a stream/counter and chooses once per admitted repeated finite attack |
46
+ | `updateTone({ toneId, variation, ... })` | Replaces the whole future-attack dictionary; amount zero disables; seed change resets the stream |
47
+ | `playChord({ variation, ... })` | Admits the whole chord atomically and chooses independent values for each voice |
48
+ | `noteOn({ variation, ... })` | Chooses at the held voice's attack; sustain and release keep that configuration |
49
+ | `playSequence({ voice: { variation, ... }, ... })` | Supplies the default for sounding steps on every native loop |
50
+ | `steps[i].overrides.variation` | Replaces the entire dictionary inherited from the sequence voice |
51
+ | `updateSequence({ voice, steps, ... })` | Changes future attacks when the revision applies |
52
+ | `updateNote({ variation, ... })` | Rejected; live edits must name the parameters they change |
53
+
54
+ Schema 1 varies `volume`, `filterHz`, `filterQ`, `fmDepth`, `noiseRate`, `lowPassHz`, `pulseWidth` and `pitchCents`. Waveform, table contents, ADSR, duration, cadence, FM ratio, routing, priority and voice count retain their authored values.
55
+
56
+ ## Dictionary reference
57
+
58
+ ```javascript
59
+ var variation = {
60
+ amount: 0.5,
61
+ seed: 42,
62
+ ranges: {
63
+ volume: [0.20, 0.30],
64
+ pitchCents: [-24, 24]
65
+ }
66
+ }
67
+ ```
68
+
69
+ | Field | Default | Accepted values |
70
+ | -------- | --------------------------------------- | ----------------------------------------------------------------------- |
71
+ | `amount` | `1` | Finite number from `0` to `1` |
72
+ | `seed` | Omitted; engine selects a native stream | Integer from `0` to `4294967295`; `0` is an explicit deterministic seed |
73
+ | `ranges` | Empty dictionary | Any subset of the eight range names below |
74
+
75
+ Only these keys are accepted. A range must contain exactly two finite numbers in ascending order. Equal endpoints are valid. Strings, booleans, `null`, `NaN`, infinity, reversed bounds and unknown names are rejected. An empty range dictionary makes no parameter changes and consumes no variation event.
76
+
77
+ The frequency ceiling is the active engine's `limits.frequency.max` value from `getDefaults()`. The maximum is `sampleRate * 0.45`, with a 19,845 Hz fallback before startup. Read `getDefaults()` again after `start()` when preparing device-specific ranges.
78
+
79
+ | Range | Inclusive validation bounds | Required source or setting |
80
+ | ------------ | --------------------------------------- | --------------------------------------------------------------------------------------- |
81
+ | `volume` | `0` to `2` | Any source; existing chord compensation follows selection |
82
+ | `filterHz` | `20` Hz to the active frequency ceiling | Enabled resonant filter: `lowpass`, `highpass` or `bandpass` |
83
+ | `filterQ` | `0.5` to `10` | Enabled resonant filter |
84
+ | `fmDepth` | `0` to `10` | `SINE`, with the normal FM ratio/depth rules |
85
+ | `noiseRate` | `1` Hz to the active frequency ceiling | `NOISE` with authored `noiseRate >= 1` |
86
+ | `lowPassHz` | `20` Hz to the active frequency ceiling | One-pole low-pass already enabled with authored `lowPassHz >= 20` |
87
+ | `pulseWidth` | `0.05` to `0.95` | `SQUARE` |
88
+ | `pitchCents` | `-1200` to `1200` cents | Resulting tuned frequencies and active pitch endpoints must remain within engine limits |
89
+
90
+ `filterHz` controls the resonant filter; `lowPassHz` controls the separate one-pole low-pass. A range cannot enable a bypassed filter or turn white noise into colored noise. Pitch cents change oscillator pitch; use `noiseRate` to change a noise source's clock. A zero-depth sine can start with an active FM-depth range when the remaining FM configuration is valid.
91
+
92
+ ## What amount means
93
+
94
+ The engine samples each named range uniformly, independently of JavaScript property order. For absolute parameters:
95
+
96
+ ```text
97
+ selected = authored + amount * (sampled - authored)
98
+ ```
99
+
100
+ For example, with authored `filterHz: 1000` and range `[600, 1400]`, amount `0.25` permits 900 to 1100 Hz; amount `0.5` permits 800 to 1200 Hz; amount `1` uses the full range. Amount describes deviation from the authored value. A range may sit entirely above or below that value, in which case the deviation is biased in that direction.
101
+
102
+ Pitch uses a multiplicative ratio after tuning and detune:
103
+
104
+ ```text
105
+ ratio = 2 ** (amount * sampledCents / 1200)
106
+ selectedFrequency = tunedFrequency * ratio
107
+ ```
108
+
109
+ The same pitch ratio applies to an active `frequencyEnd`, preserving the sweep's interval. Other range names change their starting value while the corresponding authored sweep endpoint stays fixed. For example, varying `noiseRate` does not rewrite `noiseRateEnd`.
110
+
111
+ Omitting `variation` or using a valid dictionary with `amount: 0` preserves the authored recipe and consumes no variation event. Both still obey normal noise initialization; a noise waveform can differ between attacks without this feature. Equal range endpoints force that selected field to their common value at amount `1`.
112
+
113
+ Validation checks the complete reachable configuration before admission, including every chord or step pitch, tuning, detune and active sweep endpoint. Invalid dictionaries are rejected even at amount zero. The engine does not clip invalid bounds into an acceptable recipe.
114
+
115
+ ## Repeated independent effects
116
+
117
+ Leave out `seed` to obtain successive native selections. Call `playTone` for each actual gameplay event. For periodic repetition of that finite recipe, call `repeatTone` with `intervalMs` once and retain its `toneId`; the renderer then owns the repeated deadlines. Standalone calls, including intentionally timed finite bursts, remain supported and return Boolean acceptance. `updateTone` changes future attacks, preserving the current deadline and variation counter. A changed seed restarts the stream, and dropped attacks consume no selection.
118
+
119
+ ```javascript
120
+ function fireLaser() {
121
+ var tone = {
122
+ frequency: 900, frequencyEnd: 180,
123
+ waveType: W.SQUARE, pulseWidth: 0.35,
124
+ duration: 180, attack: 2, decay: 40, sustain: 0.25,
125
+ release: 90, volume: 0.22
126
+ }
127
+ if (supportsVariation) tone.variation = {
128
+ amount: 0.6,
129
+ ranges: { pulseWidth: [0.25, 0.45], pitchCents: [-36, 36] }
130
+ }
131
+ return synth.playTone(tone)
132
+ }
133
+ fireLaser()
134
+ ```
135
+
136
+ The engine samples after admission and before initializing filters, smoothing controls and trajectories. Rejected attacks do not consume a selection. Existing release tails, capacity policy and voice replacement still apply.
137
+
138
+ ## Chords and application gain
139
+
140
+ One chord is one atomic publication. Each voice uses its position within that attack to obtain independent field samples; it does not reuse the first voice's result. If the chord cannot be admitted, none of its voices starts and no event is consumed.
141
+
142
+ Scale a volume range with the same application gain as the authored volume. Let the engine apply its existing chord compensation once.
143
+
144
+ ```javascript
145
+ var gain = 0.7
146
+ var chord = {
147
+ notes: ['C4', 'E4', 'G4'], waveType: W.TRIANGLE,
148
+ duration: 500, attack: 4, release: 120,
149
+ volume: 0.30 * gain
150
+ }
151
+ if (supportsVariation) chord.variation = {
152
+ ranges: {
153
+ volume: [0.24 * gain, 0.30 * gain],
154
+ pitchCents: [-8, 8]
155
+ }
156
+ }
157
+ synth.playChord(chord)
158
+ ```
159
+
160
+ If an effect consists of separate layers, prepare each layer's ranges and gain independently. Variation does not change how many layers or voices the effect needs.
161
+
162
+ ## Sustained voices and explicit live edits
163
+
164
+ A held voice samples once. Moving a slider, releasing another note or revising a sequence does not resample it. A live edit explicitly replaces its named parameter through the existing smoothing behavior.
165
+
166
+ ```javascript
167
+ var held = {
168
+ voiceId: 71, frequency: 220, waveType: W.SINE,
169
+ attack: 12, release: 180, volume: 0.18,
170
+ fmRatio: 2, fmDepth: 1.4
171
+ }
172
+ if (supportsVariation) held.variation = {
173
+ ranges: { fmDepth: [1.1, 1.7], pitchCents: [-10, 10] }
174
+ }
175
+ synth.noteOn(held)
176
+ synth.updateNote({ voiceId: 71, volume: 0.12 })
177
+ synth.noteOff({ voiceId: 71 })
178
+ ```
179
+
180
+ `updateNote` rejects `variation`. To obtain another varied attack, release the owned voice and start another through the normal ownership path. A sustained motor stays steady under a single held attack. Release owned IDs on cancellation, pause and teardown; keep the usual bounded handling of rejected releases.
181
+
182
+ ## Native loops and dictionary inheritance
183
+
184
+ The transport selects at sounding attacks on every native loop, including independent samples for chord voices. Rests and attacks rejected by capacity consume none. Native loop variation does not need a JavaScript timer or a loop-finished callback.
185
+
186
+ A step without `overrides.variation` inherits the root dictionary. Supplying that field replaces the whole dictionary, including its amount, ranges and seed declaration. It does not merge individual ranges. A step can therefore opt out with `{ amount: 0 }` or replace pitch variation with a filter-only dictionary.
187
+
188
+ ```javascript
189
+ var voice = {
190
+ waveType: W.SINE, attack: 2, decay: 70,
191
+ sustain: 0.2, release: 80, volume: 0.16,
192
+ fmRatio: 2, fmDepth: 1.4
193
+ }
194
+ var steps = [
195
+ { notes: [60, 67] },
196
+ { rest: true },
197
+ { notes: [64] }
198
+ ]
199
+ if (supportsVariation) {
200
+ voice.variation = {
201
+ seed: 20261101,
202
+ ranges: { fmDepth: [1.1, 1.7], pitchCents: [-12, 12] }
203
+ }
204
+ steps[2].overrides = { variation: { amount: 0 } }
205
+ }
206
+ var sequenceId = synth.playSequence({
207
+ timeUnit: 'beats', stepDuration: 0.25, loop: true,
208
+ voice: voice, steps: steps
209
+ })
210
+ ```
211
+
212
+ A revision changes future attacks when it applies. Existing voices retain their selected values. Supply the complete replacement dictionary, including a seed you intend to retain:
213
+
214
+ ```javascript
215
+ if (supportsVariation && sequenceId > 0) {
216
+ synth.updateSequence({
217
+ sequenceId: sequenceId,
218
+ voice: { variation: {
219
+ seed: 20261101, amount: 0.5,
220
+ ranges: { fmDepth: [1.1, 1.7], pitchCents: [-12, 12] }
221
+ } }
222
+ })
223
+ }
224
+ ```
225
+
226
+ The root voice merges with the existing sequence voice on revision. Omitting `variation` from an updated root does not remove a dictionary already stored there. To clear it for future inherited attacks, publish an amount-zero replacement. Explicit step dictionaries still take precedence; revise those steps as well when disabling the whole score.
227
+
228
+ ```javascript
229
+ if (supportsVariation && sequenceId > 0) {
230
+ synth.updateSequence({
231
+ sequenceId: sequenceId,
232
+ voice: { variation: { amount: 0 } }
233
+ })
234
+ }
235
+ ```
236
+
237
+ Stop the owned sequence through the normal lifecycle when it is no longer needed. Stop/new play starts a new stream.
238
+
239
+ ## Seeds and reproducibility
240
+
241
+ | Usage | Reproducibility |
242
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
243
+ | Repeated standalone calls with the same recipe and explicit seed | Reproduce the selected parameter values, starting at event zero each time |
244
+ | Standalone calls without a seed | Use successive native selections |
245
+ | Seeded native loop | Advances its attack counter across loop iterations |
246
+ | Revision with unchanged seed identity | Retains that counter |
247
+ | Revision that changes the root seed | Resets the counter when the revision applies |
248
+ | Stop followed by a new sequence | Starts a new counter |
249
+
250
+ Seed identity includes whether the root seed was declared and its value, plus the sorted multiset of explicit step seed declarations. Moving steps, swapping existing declarations or inserting inherited rests preserves that identity. Adding, removing or changing an explicit step seed declaration changes it and resets the counter. Prefer a root seed for comparisons of a complete score.
251
+
252
+ The counter is an unsigned 64-bit value and wraps modulo `2^64`. Admitted attack, voice ordinal and field slot determine each sample through the shared integer helper. Field slots are stable, so changing dictionary property order or adding an unrelated range does not shift existing field samples. Variation state is separate from the noise generator. A reproducible seed reproduces selected parameters. It does not promise identical noise PCM or identical DSP output across platforms.
253
+
254
+ ## Rejected configurations
255
+
256
+ These examples are intentionally invalid when variation is supported:
257
+
258
+ ```javascript
259
+ // NOISE is required for a noiseRate range.
260
+ synth.playTone({ waveType: W.SINE,
261
+ variation: { ranges: { noiseRate: [1000, 2000] } } })
262
+
263
+ // Zero amount still validates the inverted range.
264
+ synth.playTone({ variation: {
265
+ amount: 0, ranges: { volume: [0.4, 0.2] }
266
+ } })
267
+
268
+ // Live updates do not accept an attack-only dictionary.
269
+ synth.updateNote({ voiceId: 71,
270
+ variation: { ranges: { pitchCents: [-10, 10] } } })
271
+ ```
272
+
273
+ Also check pitch corners near 1 Hz or the reported ceiling. A valid base pitch can become invalid when its tuning, detune and maximum pitch offset combine. Keep module diagnostics when a recipe is rejected; a `false` return can also mean unavailable engine state or a full command queue.
274
+
275
+ ## Studio and game integration
276
+
277
+ Studio calls this control Variantes. Its displayed percentage maps linearly: 25% sends native amount `0.25`, 50% sends `0.5`, and 100% sends `1`. Studio prepares broader ranges for its editor. The games retain their authored range profiles.
278
+
279
+ Frontier automatically applies authored ranges only to its eligible destruction sounds; Guardian applies them only to its two lasers. Both require the capability/schema pair and keep their existing recipes on older modules. Studio allows any source to opt in through compatible parameter ranges. See [Studio's control and preset guide](https://github.com/macCesar/TiSynth-Studio/blob/main/documentation/variation.md); the native contract in this document does not depend on a consumer app.
280
+
281
+ ## Implementation and verification
282
+
283
+ Both renderers use [`shared/synth_variation.h`](https://github.com/macCesar/TiSynthEngine/blob/7cd035409453b01df2736aef56f2316d94ca019c/shared/synth_variation.h): stable schema slots, integer-defined SplitMix64 finalization and exact uint32-to-uniform conversion. Parsing prepares a fixed 144-byte configuration outside the audio callback. The callback performs bounded arithmetic without allocation, locks, JavaScript calls or entropy services.
284
+
285
+ Focused checks cover known vectors, both native validators, attack admission, chord ordinals, sustained stability, loops, revisions, rejection and old-renderer compatibility with controlled noise initialization. [Technical verification](native-variation-validation.md) records the measured memory/callback cost and its limits. Host results do not certify physical latency or listening quality on every device.
@@ -337,7 +337,9 @@ This repeated chord uses one centered native sequence. The one-off layered chord
337
337
 
338
338
  ### Cancellable repetition
339
339
 
340
- Every repeating sound has an identified native owner. Repeated start requests coalesce until the matching retirement event frees its slot. Stop its sequence ID and cancel pending restarts when its window closes; no JavaScript timer schedules subsequent notes.
340
+ The chord pulse above has an identified sequence owner. Repeated start requests coalesce until its matching `sequenceend` retirement frees the slot. Stop its sequence ID and cancel pending restarts when its window closes; its native sequence schedules subsequent chord attacks.
341
+
342
+ For repetition of an original finite tone in API 1.1.0, use `repeatTone({ intervalMs, ...recipe })`, retain its positive tone ID, edit future attacks with `updateTone`, and cancel with `stopTone`. Each independent layer retains its own tone ID and overlap budget. `stopTone` preserves admitted finite attacks; `stopSequence` releases its sequence's voices. Tone IDs have no completion event, so the sequence retirement/restart procedure above does not apply to them. Independent event calls and intentionally timed finite JavaScript bursts remain supported; cancel any app-owned timers at teardown. See [the repetition contract](api.md#finite-tone-repetition) and [complete effect fidelity](instruments-and-effects.md#repeated-firing-uses-the-native-clock).
341
343
 
342
344
  ```javascript
343
345
  function cleanupAudio () {
@@ -1,7 +1,8 @@
1
1
  # Curated sound and interaction recipes
2
2
 
3
- These recipes are tuned starting points for `ti.synthengine` 1.0.0. They use
4
- only public option keys, keep every explicit envelope valid, and obtain
3
+ These recipes use the core public contract shared by `ti.synthengine`
4
+ 1.0.2 / API 1.0.0 and 1.1.0 / API 1.1.0. They use only public option keys,
5
+ keep every explicit envelope valid, and obtain
5
6
  waveforms from the runtime contract instead of hardcoding integers. Install the
6
7
  matching official Android/iOS packages and follow the owning-engine
7
8
  [installation and lifecycle guide](guide.md) before using them.