@maccesar/titools 5.2.3 → 5.2.4
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.
- package/README.md +85 -81
- package/docs/examples.md +3 -1
- package/package.json +2 -2
- package/skills/ti-synthengine/SKILL.md +18 -5
- package/skills/ti-synthengine/assets/example/README.md +11 -4
- package/skills/ti-synthengine/assets/example/app.js +20 -0
- package/skills/ti-synthengine/references/advanced-examples.md +27 -2
- package/skills/ti-synthengine/references/api.md +237 -226
- package/skills/ti-synthengine/references/audio-engine.md +15 -8
- package/skills/ti-synthengine/references/audio-pipeline-validation.md +1 -1
- package/skills/ti-synthengine/references/examples.md +1 -1
- package/skills/ti-synthengine/references/guide.md +21 -4
- package/skills/ti-synthengine/references/instruments-and-effects.md +157 -0
- package/skills/ti-synthengine/references/native-variation-proposal.md +182 -0
- package/skills/ti-synthengine/references/native-variation-validation.md +63 -0
- package/skills/ti-synthengine/references/native-variation.md +283 -0
- package/skills/ti-synthengine/references/recommendations.md +2 -2
- package/skills/ti-synthengine/references/sound-design.md +15 -13
- package/skills/ti-synthengine/references/source-notes.md +27 -35
- package/skills/ti-synthengine/references/source-snapshot.json +145 -180
- package/skills/ti-synthengine/references/stats-performance.md +1 -1
- package/skills/ti-synthengine/references/troubleshooting.md +31 -23
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `b07c97c762be5905f132cecf33e9d60ea0c21316` (2026-11-02). Published 1.0.2; local 1.1.0 candidates; public API 1.0.0. Upstream `documentation/native-variation.md` SHA-256: `30bbf837a163372aa0d369c6c8e267dc8fc2d3349e8ae30c51d13d20c88cb7eb`.
|
|
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 local 1.1.0 packages, with public API version `1.0.0` and variation schema version `1`. The published 1.0.2 packages do not accept it. The local packages have not been published as a release.
|
|
8
|
+
|
|
9
|
+
## Select the local package
|
|
10
|
+
|
|
11
|
+
Install the matching locally built 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; the new property does not change the reported public API version.
|
|
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 following examples 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
|
+
| `playChord({ variation, ... })` | Admits the whole chord atomically and chooses independent values for each voice |
|
|
46
|
+
| `noteOn({ variation, ... })` | Chooses at the held voice's attack; sustain and release keep that configuration |
|
|
47
|
+
| `playSequence({ voice: { variation, ... }, ... })` | Supplies the default for sounding steps on every native loop |
|
|
48
|
+
| `steps[i].overrides.variation` | Replaces the entire dictionary inherited from the sequence voice |
|
|
49
|
+
| `updateSequence({ voice, steps, ... })` | Changes future attacks when the revision applies |
|
|
50
|
+
| `updateVoice({ variation, ... })` | Rejected; live edits must name the parameters they change |
|
|
51
|
+
|
|
52
|
+
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.
|
|
53
|
+
|
|
54
|
+
## Dictionary reference
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
var variation = {
|
|
58
|
+
amount: 0.5,
|
|
59
|
+
seed: 42,
|
|
60
|
+
ranges: {
|
|
61
|
+
volume: [0.20, 0.30],
|
|
62
|
+
pitchCents: [-24, 24]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Field | Default | Accepted values |
|
|
68
|
+
| --- | --- | --- |
|
|
69
|
+
| `amount` | `1` | Finite number from `0` to `1` |
|
|
70
|
+
| `seed` | Omitted; engine selects a native stream | Integer from `0` to `4294967295`; `0` is an explicit deterministic seed |
|
|
71
|
+
| `ranges` | Empty dictionary | Any subset of the eight range names below |
|
|
72
|
+
|
|
73
|
+
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.
|
|
74
|
+
|
|
75
|
+
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.
|
|
76
|
+
|
|
77
|
+
| Range | Inclusive validation bounds | Required source or setting |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `volume` | `0` to `2` | Any source; existing chord compensation follows selection |
|
|
80
|
+
| `filterHz` | `20` Hz to the active frequency ceiling | Enabled resonant filter: `lowpass`, `highpass` or `bandpass` |
|
|
81
|
+
| `filterQ` | `0.5` to `10` | Enabled resonant filter |
|
|
82
|
+
| `fmDepth` | `0` to `10` | `SINE`, with the normal FM ratio/depth rules |
|
|
83
|
+
| `noiseRate` | `1` Hz to the active frequency ceiling | `NOISE` with authored `noiseRate >= 1` |
|
|
84
|
+
| `lowPassHz` | `20` Hz to the active frequency ceiling | One-pole low-pass already enabled with authored `lowPassHz >= 20` |
|
|
85
|
+
| `pulseWidth` | `0.05` to `0.95` | `SQUARE` |
|
|
86
|
+
| `pitchCents` | `-1200` to `1200` cents | Resulting tuned frequencies and active pitch endpoints must remain within engine limits |
|
|
87
|
+
|
|
88
|
+
`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.
|
|
89
|
+
|
|
90
|
+
## What amount means
|
|
91
|
+
|
|
92
|
+
The engine samples each named range uniformly, independently of JavaScript property order. For absolute parameters:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
selected = authored + amount * (sampled - authored)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
Pitch uses a multiplicative ratio after tuning and detune:
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
ratio = 2 ** (amount * sampledCents / 1200)
|
|
104
|
+
selectedFrequency = tunedFrequency * ratio
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
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`.
|
|
108
|
+
|
|
109
|
+
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`.
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
## Repeated independent effects
|
|
114
|
+
|
|
115
|
+
Leave out `seed` to obtain successive native selections. Call `playTone` for each actual shot; keep the application's existing firing clock.
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
function fireLaser() {
|
|
119
|
+
var tone = {
|
|
120
|
+
frequency: 900, frequencyEnd: 180,
|
|
121
|
+
waveType: W.SQUARE, pulseWidth: 0.35,
|
|
122
|
+
duration: 180, attack: 2, decay: 40, sustain: 0.25,
|
|
123
|
+
release: 90, volume: 0.22
|
|
124
|
+
}
|
|
125
|
+
if (supportsVariation) tone.variation = {
|
|
126
|
+
amount: 0.6,
|
|
127
|
+
ranges: { pulseWidth: [0.25, 0.45], pitchCents: [-36, 36] }
|
|
128
|
+
}
|
|
129
|
+
return synth.playTone(tone)
|
|
130
|
+
}
|
|
131
|
+
fireLaser()
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
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.
|
|
135
|
+
|
|
136
|
+
## Chords and application gain
|
|
137
|
+
|
|
138
|
+
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.
|
|
139
|
+
|
|
140
|
+
Scale a volume range with the same application gain as the authored volume. Let the engine apply its existing chord compensation once.
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
var gain = 0.7
|
|
144
|
+
var chord = {
|
|
145
|
+
notes: ['C4', 'E4', 'G4'], waveType: W.TRIANGLE,
|
|
146
|
+
duration: 500, attack: 4, release: 120,
|
|
147
|
+
volume: 0.30 * gain
|
|
148
|
+
}
|
|
149
|
+
if (supportsVariation) chord.variation = {
|
|
150
|
+
ranges: {
|
|
151
|
+
volume: [0.24 * gain, 0.30 * gain],
|
|
152
|
+
pitchCents: [-8, 8]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
synth.playChord(chord)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
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.
|
|
159
|
+
|
|
160
|
+
## Sustained voices and explicit live edits
|
|
161
|
+
|
|
162
|
+
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.
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
var held = {
|
|
166
|
+
voiceId: 71, frequency: 220, waveType: W.SINE,
|
|
167
|
+
attack: 12, release: 180, volume: 0.18,
|
|
168
|
+
fmRatio: 2, fmDepth: 1.4
|
|
169
|
+
}
|
|
170
|
+
if (supportsVariation) held.variation = {
|
|
171
|
+
ranges: { fmDepth: [1.1, 1.7], pitchCents: [-10, 10] }
|
|
172
|
+
}
|
|
173
|
+
synth.noteOn(held)
|
|
174
|
+
synth.updateVoice({ voiceId: 71, volume: 0.12 })
|
|
175
|
+
synth.noteOff({ voiceId: 71 })
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`updateVoice` 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.
|
|
179
|
+
|
|
180
|
+
## Native loops and dictionary inheritance
|
|
181
|
+
|
|
182
|
+
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.
|
|
183
|
+
|
|
184
|
+
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.
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
var voice = {
|
|
188
|
+
waveType: W.SINE, attack: 2, decay: 70,
|
|
189
|
+
sustain: 0.2, release: 80, volume: 0.16,
|
|
190
|
+
fmRatio: 2, fmDepth: 1.4
|
|
191
|
+
}
|
|
192
|
+
var steps = [
|
|
193
|
+
{ notes: [60, 67] },
|
|
194
|
+
{ rest: true },
|
|
195
|
+
{ notes: [64] }
|
|
196
|
+
]
|
|
197
|
+
if (supportsVariation) {
|
|
198
|
+
voice.variation = {
|
|
199
|
+
seed: 20261101,
|
|
200
|
+
ranges: { fmDepth: [1.1, 1.7], pitchCents: [-12, 12] }
|
|
201
|
+
}
|
|
202
|
+
steps[2].overrides = { variation: { amount: 0 } }
|
|
203
|
+
}
|
|
204
|
+
var sequenceId = synth.playSequence({
|
|
205
|
+
timeUnit: 'beats', stepDuration: 0.25, loop: true,
|
|
206
|
+
voice: voice, steps: steps
|
|
207
|
+
})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
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:
|
|
211
|
+
|
|
212
|
+
```javascript
|
|
213
|
+
if (supportsVariation && sequenceId > 0) {
|
|
214
|
+
synth.updateSequence({
|
|
215
|
+
sequenceId: sequenceId,
|
|
216
|
+
voice: { variation: {
|
|
217
|
+
seed: 20261101, amount: 0.5,
|
|
218
|
+
ranges: { fmDepth: [1.1, 1.7], pitchCents: [-12, 12] }
|
|
219
|
+
} }
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
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.
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
if (supportsVariation && sequenceId > 0) {
|
|
228
|
+
synth.updateSequence({
|
|
229
|
+
sequenceId: sequenceId,
|
|
230
|
+
voice: { variation: { amount: 0 } }
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Stop the owned sequence through the normal lifecycle when it is no longer needed. Stop/new play starts a new stream.
|
|
236
|
+
|
|
237
|
+
## Seeds and reproducibility
|
|
238
|
+
|
|
239
|
+
| Usage | Reproducibility |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| Repeated standalone calls with the same recipe and explicit seed | Reproduce the selected parameter values, starting at event zero each time |
|
|
242
|
+
| Standalone calls without a seed | Use successive native selections |
|
|
243
|
+
| Seeded native loop | Advances its attack counter across loop iterations |
|
|
244
|
+
| Revision with unchanged seed identity | Retains that counter |
|
|
245
|
+
| Revision that changes the root seed | Resets the counter when the revision applies |
|
|
246
|
+
| Stop followed by a new sequence | Starts a new counter |
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
|
|
250
|
+
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 promises selected parameters, not identical noise PCM or identical DSP output across platforms.
|
|
251
|
+
|
|
252
|
+
## Rejected configurations
|
|
253
|
+
|
|
254
|
+
These examples are intentionally invalid when variation is supported:
|
|
255
|
+
|
|
256
|
+
```javascript
|
|
257
|
+
// NOISE is required for a noiseRate range.
|
|
258
|
+
synth.playTone({ waveType: W.SINE,
|
|
259
|
+
variation: { ranges: { noiseRate: [1000, 2000] } } })
|
|
260
|
+
|
|
261
|
+
// Zero amount still validates the inverted range.
|
|
262
|
+
synth.playTone({ variation: {
|
|
263
|
+
amount: 0, ranges: { volume: [0.4, 0.2] }
|
|
264
|
+
} })
|
|
265
|
+
|
|
266
|
+
// Live updates do not accept an attack-only dictionary.
|
|
267
|
+
synth.updateVoice({ voiceId: 71,
|
|
268
|
+
variation: { ranges: { pitchCents: [-10, 10] } } })
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
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.
|
|
272
|
+
|
|
273
|
+
## Studio and game integration
|
|
274
|
+
|
|
275
|
+
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.
|
|
276
|
+
|
|
277
|
+
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.
|
|
278
|
+
|
|
279
|
+
## Implementation and verification
|
|
280
|
+
|
|
281
|
+
Both renderers use [`shared/synth_variation.h`](https://github.com/macCesar/TiSynthEngine/blob/433d7884800f70fb4803dbfe2e0e869cd5411889/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.
|
|
282
|
+
|
|
283
|
+
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.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `b07c97c762be5905f132cecf33e9d60ea0c21316` (2026-11-02). Published 1.0.2; local 1.1.0 candidates; public API 1.0.0. Upstream `documentation/recommendations.md` SHA-256: `d3d508efe07fce012baceb44285aa9ab4b850cc5c53a5d14c9b8d193e38ca8fb`.
|
|
2
2
|
|
|
3
3
|
# Recommendations
|
|
4
4
|
|
|
@@ -87,7 +87,7 @@ Do not copy the frequency maximum into application code. It depends on the activ
|
|
|
87
87
|
|
|
88
88
|
The same rule applies to `noiseRate`, `lowPassHz` and `filterHz`. Validate a custom `waveTable` as 2 to 64 finite values from -1 to 1, and remember that `bitDepth` accepts zero or an integer from 2 to 16. For `detune`, check the resulting pitch too: a shifted pitch that leaves the frequency range is rejected even when the cents value is in range.
|
|
89
89
|
|
|
90
|
-
Check
|
|
90
|
+
Check each method's documented return value during development: Boolean publication acceptance or a positive sequence ID, with 0 rejecting a sequence. Methods that emit structured validation warnings include the method, key and reason in the native log, but some sequence/control rejection paths do not log. Logging the return at the caller helps identify a stale preset or misspelled field without treating silence in the native log as acceptance.
|
|
91
91
|
|
|
92
92
|
## Test the intended sound
|
|
93
93
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `b07c97c762be5905f132cecf33e9d60ea0c21316` (2026-11-02). Published 1.0.2; local 1.1.0 candidates; public API 1.0.0. Upstream `documentation/sound-design.md` SHA-256: `3ad60c006e6ba296110b73362ebba74a30fd5e722fae6c5066cec815e500262a`.
|
|
2
2
|
|
|
3
3
|
# Sound design with TiSynthEngine
|
|
4
4
|
|
|
@@ -6,6 +6,8 @@ TiSynthEngine generates sound with oscillators, without recorded samples. Pitch,
|
|
|
6
6
|
|
|
7
7
|
Additional [curated recipes](recipes.md) provide application sound and interaction variants.
|
|
8
8
|
|
|
9
|
+
For the distinction between a playable timbre and a complete effect recipe, start with [instruments, sound effects and complete sequences](instruments-and-effects.md). It covers held keys, independent shots, native sequences, layered effects, demonstrations and shared output headroom.
|
|
10
|
+
|
|
9
11
|
The examples assume the engine is running and use these constants:
|
|
10
12
|
|
|
11
13
|
```javascript
|
|
@@ -137,27 +139,27 @@ synth.playTone({
|
|
|
137
139
|
|
|
138
140
|
## Envelope and total duration
|
|
139
141
|
|
|
140
|
-
|
|
142
|
+
A timed tone or chord uses an attack, an implicit sustain section and a release (with an optional decay stage described below):
|
|
141
143
|
|
|
142
144
|
```text
|
|
143
145
|
silence -> attack -> full level -> release -> silence
|
|
144
146
|
|<---------- duration ---------->|
|
|
145
147
|
```
|
|
146
148
|
|
|
147
|
-
`duration` is the total time, not the sustain time. If duration is 500 ms, attack is 50 ms and release is 100 ms, the full-level middle lasts 350 ms.
|
|
149
|
+
`duration` on `playTone` and `playChord` is the total time, not the sustain time. If duration is 500 ms, attack is 50 ms and release is 100 ms, the full-level middle lasts 350 ms. Held voices instead release on `noteOff`; native sequence steps use `duration` for the next-attack interval and `length` or gate for the note deadline. Their release can overlap later attacks. See [the playback contracts](instruments-and-effects.md#3-complete-sound-sequences-and-layered-effects).
|
|
148
150
|
|
|
149
151
|
Short attacks feel immediate. A 0 or 1 ms attack can be useful for percussion, but an abrupt start at an unlucky phase can sound like a click. Values between 5 and 15 ms keep most game feedback crisp without a hard edge.
|
|
150
152
|
|
|
151
153
|
Long attacks soften the onset. Use 100 to 500 ms for pads and slow warnings. Release controls how the sound leaves: 20 to 80 ms feels tight, while several hundred milliseconds leaves a tail.
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
For timed tones and chords, explicit attack and release values must fit. These relationships are required:
|
|
154
156
|
|
|
155
157
|
```text
|
|
156
158
|
attack < duration
|
|
157
159
|
release <= duration - attack
|
|
158
160
|
```
|
|
159
161
|
|
|
160
|
-
|
|
162
|
+
Timed defaults are adjusted internally if a very short duration leaves no room; explicit values must pass validation. Native rendering has additional edge rules: held and sequence voices use at least 3 ms attack/release, while iOS timed conversion rounds an accepted zero attack/release to at least one sample and Android preserves zero. Validator acceptance does not establish identical sample envelopes.
|
|
161
163
|
|
|
162
164
|
## Decay and sustain
|
|
163
165
|
|
|
@@ -177,7 +179,7 @@ Practical starting regions:
|
|
|
177
179
|
|
|
178
180
|
`sustain` below 1 needs a nonzero `decay`. Without one the level would jump instantly, which clicks, so the call is rejected with `sustain_without_decay`.
|
|
179
181
|
|
|
180
|
-
The release scales the level the note has reached. If the release begins while the decay is still running, the note leaves smoothly from that point.
|
|
182
|
+
The release scales the level the note has reached. If the release begins while the decay is still running, the note leaves smoothly from that point. For timed tones and chords, the decay still has to fit: `attack` plus `decay` cannot exceed `duration`, and the 50 ms default attack counts. Set a short `attack` explicitly for a tight pluck.
|
|
181
183
|
|
|
182
184
|
## Pan and movement
|
|
183
185
|
|
|
@@ -241,7 +243,7 @@ Filters shape harmonics, so give them a source that has some: `SAWTOOTH`, `SQUAR
|
|
|
241
243
|
|
|
242
244
|
`filterQ` is the resonance. The default 0.7071 is flat. Values from 2 to 4 add character, and above about 6 the filter whistles around the cutoff. A low-pass or high-pass boosts the cutoff region by about `filterQ`, so Q 10 is roughly 20 dB. Lower the event `volume` as Q goes up. The limiter catches peaks, but a resonance that keeps hitting it makes the whole mix pump. The band-pass keeps unity gain at its center for every Q and only narrows.
|
|
243
245
|
|
|
244
|
-
`filterHzEnd` moves the cutoff over the whole event. The motion is exponential, so each second covers the same musical distance. Set it above `filterHz` to open the filter and below to close it. Zero disables the sweep.
|
|
246
|
+
For timed tones and chords, `filterHzEnd` moves the cutoff over the whole event. A native sequence sweep instead uses the note length and holds its endpoint during release. The motion is exponential, so each second covers the same musical distance. Set it above `filterHz` to open the filter and below to close it. Zero disables the sweep.
|
|
245
247
|
|
|
246
248
|
```javascript
|
|
247
249
|
synth.playTone({
|
|
@@ -636,12 +638,12 @@ The two additional voice LFOs move filter cutoff, square PWM or sine FM index us
|
|
|
636
638
|
|
|
637
639
|
## Native space and creative drive
|
|
638
640
|
|
|
639
|
-
| Processor
|
|
640
|
-
|
|
|
641
|
-
| Stereo chorus
|
|
642
|
-
| Ping-pong delay
|
|
643
|
-
| Stereo reverb
|
|
644
|
-
| Optional saturation | Soft waveshaping adds harmonics and changes attack/timbre.
|
|
641
|
+
| Processor | What changes | Useful starting region |
|
|
642
|
+
| ------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
643
|
+
| Stereo chorus | A short interpolated delay with offset LFO phases adds width and movement. | Rate 0.3 to 0.8 Hz, depth 0.15 to 0.35, mix 0.08 to 0.2. |
|
|
644
|
+
| Ping-pong delay | Alternating echoes create rhythmic or spatial repeats. | 180 to 350 ms or synced eighths, feedback 0.15 to 0.35, mix 0.08 to 0.18. |
|
|
645
|
+
| Stereo reverb | Damped comb/allpass reflections create a diffuse decay. | Decay 0.8 to 1.8 seconds, damping 0.5 to 0.7, mix 0.08 to 0.18. |
|
|
646
|
+
| Optional saturation | Soft waveshaping adds harmonics and changes attack/timbre. | Drive 1.2 to 1.6 with a small blend; compare at matched levels. |
|
|
645
647
|
|
|
646
648
|
All processors are off initially, with input gain 1. Enable one shared chain through `setMasterProcessing`; select `applyTo` categories such as `music` and `ambience` to keep explosions dry. Per-voice `effectSend` overrides that category selection. [Configuration](advanced-examples.md#chorus-delay-reverb-and-creative-saturation) and [exact ranges](api.md#setmasterprocessingoptions) explain chorus, delay, reverb and optional saturation.
|
|
647
649
|
|
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
# Maintaining the synthesis snapshot
|
|
2
2
|
|
|
3
|
-
These references
|
|
4
|
-
The corrected official documentation is a reviewed working tree based on
|
|
5
|
-
`4a45f21230ffc5a8838fbd5fc108dd3ab470362a`, recorded with its exact fingerprint in
|
|
6
|
-
[source-snapshot.json](source-snapshot.json). The documentation corrections are
|
|
7
|
-
not represented as committed by that base revision. Review date: 2026-10-29.
|
|
3
|
+
These references cover **published module 1.0.2 and local 1.1.0 candidates**, both with public API 1.0.0. All fifteen official Markdown documents were reviewed against the native module. Thirteen are mirrored into fourteen parts; the index and notices retain explicit reviewed exclusions. The official documentation corrections are committed at `b07c97c762be5905f132cecf33e9d60ea0c21316`. Complete source/mirror hashes are recorded in [source-snapshot.json](source-snapshot.json). Review date: 2026-11-02.
|
|
8
4
|
|
|
9
5
|
## Public downloads and artifact provenance
|
|
10
6
|
|
|
11
|
-
The replacement packages were
|
|
12
|
-
on 2026-10-30 UTC (2026-10-29 locally), rather than reusing cached 1.0.2 files.
|
|
7
|
+
The published replacement packages were downloaded and verified on 2026-10-30 UTC (2026-10-29 locally). The following records are that historical artifact verification, not new downloads during this audit. GitHub release metadata was checked on 2026-11-02: public platform releases remain 1.0.2; local 1.1.0 candidates have no published release.
|
|
13
8
|
|
|
14
9
|
| Platform | Published release | Replacement package SHA-256 |
|
|
15
10
|
| --- | --- | --- |
|
|
16
11
|
| Android | [android-1.0.2](https://github.com/macCesar/TiSynthEngine/releases/tag/android-1.0.2) | `8698104f188782a77699fbd6308a779ca0b190833ceebf398557b628af1b8bbe` |
|
|
17
12
|
| iOS / Mac Catalyst | [ios-1.0.2](https://github.com/macCesar/TiSynthEngine/releases/tag/ios-1.0.2) | `779e77d018d5b3f686d69cb1b4b0162281803ea12691487df565c1fd15175c58` |
|
|
18
13
|
|
|
19
|
-
Both ZIPs passed full CRC checks and
|
|
20
|
-
digests. These hashes replace the original 1.0.2 packages' hashes; filenames and
|
|
14
|
+
Both ZIPs passed full CRC checks and matched the GitHub asset SHA-256 digests at that verification. These hashes replace the original 1.0.2 packages' hashes; filenames and
|
|
21
15
|
release URLs did not change. Manifests report module `ti.synthengine`, version
|
|
22
16
|
`1.0.2`, minimum Titanium `13.2.0.GA`, four Android ABIs and Apple Mac Catalyst
|
|
23
17
|
support. The manifest's native `apiversion` field is distinct from the JavaScript
|
|
@@ -32,15 +26,14 @@ app-root ZIP installation and both `tiapp.xml` declarations.
|
|
|
32
26
|
|
|
33
27
|
## Sources and permitted transformations
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
ordered examples/advanced-examples split. The current snapshot inventories all
|
|
29
|
+
Thirteen official Markdown documents are mirrored into fourteen parts, including the ordered examples/advanced-examples split. New references preserve the complete [instruments/effects guide](instruments-and-effects.md), [implemented variation contract](native-variation.md), [historical proposal](native-variation-proposal.md) and [variation verification](native-variation-validation.md). The proposal is historical design context, not an outstanding implementation plan. The current snapshot inventories all
|
|
37
30
|
complete JSON reports, bundled example assets and linked raw-evidence archives.
|
|
38
|
-
The
|
|
31
|
+
The historical [performance review](performance-review.json) and its raw archive are
|
|
39
32
|
preserved alongside historical reports; none is replaced by a favorable summary.
|
|
40
33
|
Read snapshot records for exact counts and hashes. Example JavaScript and raw
|
|
41
34
|
reports/archives remain byte-identical to source.
|
|
42
35
|
|
|
43
|
-
Each Markdown part and the example README have a
|
|
36
|
+
Each Markdown part and the example README have a committed-source provenance header and recorded navigation transformations. Verified API, timing, spatial, panic, processing and diagnostic corrections were applied to the official module documentation before synchronization. They are shared source content, not mirror-only errata. Remaining `link_fixes` record navigation and curated entry points; the source hash identifies the corrected official committed text. Reversible `link_fixes` rebase links into the split advanced examples, preserve [curated recipes](recipes.md), and resolve current example links to bundled assets. The example README points back to this skill's current API and guides. All three example JavaScript files remain byte-identical to source; none requires a historical GitHub snapshot.
|
|
44
37
|
|
|
45
38
|
The fidelity regression strips provenance/navigation, concatenates example parts, reverses declared transformations and compares the complete source hash. Raw records must remain byte-identical. Update transformations intentionally; never change hashes merely to hide unexplained source drift.
|
|
46
39
|
|
|
@@ -82,11 +75,10 @@ fixed-gain recommendations target the measured 1–8-voice configuration, with
|
|
|
82
75
|
underrun remains unresolved, including independent silent Oboe reproduction.
|
|
83
76
|
Route-wide latency, Bluetooth and extended maximum-load acceptance remain open.
|
|
84
77
|
|
|
85
|
-
Official corrections
|
|
78
|
+
Official documentation corrections preserve panic cancellation (`stopAll` stops every
|
|
86
79
|
sequence and invalidates effect histories), compatible live `updateVoice`
|
|
87
80
|
noise/low-pass/spatial/send edits, and category selection with `applyTo` plus
|
|
88
|
-
explicit `effectSend` overrides.
|
|
89
|
-
remaining native-backed errata and curated recipe navigation stay reversible.
|
|
81
|
+
explicit `effectSend` overrides. The matching mirror-only errata were removed from the transformation list; curated recipe navigation remains reversible.
|
|
90
82
|
Android and Apple production callback tests now run in CI; the historical Java
|
|
91
83
|
DSP model is not the production audio oracle. No native binaries were installed
|
|
92
84
|
or device/listening tests rerun during this skill update.
|
|
@@ -110,12 +102,10 @@ bypass, memory and strict Frontier presentation criteria. Current evidence and
|
|
|
110
102
|
rejected candidates must not be combined into a general pass. This skill update
|
|
111
103
|
did not rerun devices, install binaries or alter native source.
|
|
112
104
|
|
|
113
|
-
The
|
|
114
|
-
1.0.2 source on October 29. These current differences remain explicit;
|
|
115
|
-
hash fidelity alone cannot establish them:
|
|
105
|
+
The native differences below were rechecked against current sources on November 2 and are now documented accurately in the official API/engine guides as well as the mirrors. They describe implementation differences, not unresolved documentation contradictions:
|
|
116
106
|
|
|
117
|
-
- iOS timed tone/chord command construction
|
|
118
|
-
and release to
|
|
107
|
+
- iOS timed tone/chord command construction initially rounds accepted zero attack
|
|
108
|
+
and release to one sample before duration-budget trimming; Android preserves zero. Validator
|
|
119
109
|
acceptance does not establish identical sample envelopes.
|
|
120
110
|
- iOS early tempo/sequence/processing rejections and incompatible held spatial/
|
|
121
111
|
modulation edits can bypass rejection counters. Android also leaves some
|
|
@@ -124,21 +114,23 @@ hash fidelity alone cannot establish them:
|
|
|
124
114
|
- Sequence cutoff coefficients currently refresh every 16 samples on Android
|
|
125
115
|
and every 8 samples on Apple; both use note length but do not promise identical
|
|
126
116
|
sample output. See [engine details](audio-engine.md).
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- Remaining stale official claims are corrected only where verified against
|
|
133
|
-
current native behavior, with exact reversible transformations. Corrections
|
|
134
|
-
already adopted upstream are removed from the transformation list.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
These findings are skill corrections and module implementation findings, not
|
|
138
|
-
native fixes. No module code, public range or hardware acceptance was changed.
|
|
117
|
+
- Local 1.1.0 returns `capabilities.variation: true` and `variationSchemaVersion: 1` on both platforms. Android also reports native sequence/master processing/modulation flags and `limits.stepDurationMs`; iOS omits those additional entries. Published 1.0.2 does not accept variation. Both platforms return sequence-capacity, priority and send limits; use the [API](api.md) for exact beat/millisecond ranges.
|
|
118
|
+
- Official examples now bind their waveform constants and document native sequence modulation, complete effect fidelity and publication ownership correctly. The mirrors preserve the same facts.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
These are official-documentation and skill corrections, not native implementation fixes. No module code, public range or hardware acceptance was changed.
|
|
139
122
|
A validator corpus establishes accepted/rejected dictionaries, not sample-identical
|
|
140
|
-
rendering on a physical device.
|
|
141
|
-
|
|
123
|
+
rendering on a physical device. Current source-first coverage ledgers and native locators are recorded in the maintainer's November 2 audit evidence.
|
|
124
|
+
|
|
125
|
+
## Local 1.1.0 attack variation
|
|
126
|
+
|
|
127
|
+
Both platforms validate eight bounded parameter ranges and select once per admitted attack. Require the capability/schema pair and omit the property entirely on published 1.0.2, including amount zero. The [variation guide](native-variation.md) preserves exact bounds, linear amount, pitch ratio, source prerequisites, per-chord ordinals, held stability, whole-dictionary step replacement, revision merging and deterministic seed identity. `updateVoice` rejects the attack dictionary. Preserve all of the authored envelope, cadence, routing and layer gains.
|
|
128
|
+
|
|
129
|
+
The [verification report](native-variation-validation.md) separates initial host/build checks from later consumer installations and scoped listening feedback. It records the 144-byte configuration and four-transport growth from 8,627,712 to 9,537,152 bytes, excluding other engine/bridge/process storage. Earlier 1.0.2 memory reports remain historical. Host callback timings do not prove device overhead or sustained acceptance.
|
|
130
|
+
|
|
131
|
+
This audit reran the paired production validators (156 cases per platform), the affected documentation checks and TiTools mirror/recipe checks. New source inspection verified sampling/admission and sequence seed identity. Historical renderer PCM, native build, consumer install and listening results were read with their original boundaries; they were not repeated or expanded into general device acceptance. No native source or binary was changed.
|
|
132
|
+
|
|
133
|
+
All thirteen raw JSON/ZIP records moved upstream from `documentation/` to `docs/validation/` without changing bytes. Their distributed skill paths are preserved; the snapshot now records their actual upstream locations. All five ZIPs passed CRC checks. The three example JavaScript assets remain byte-identical; `app.js` now includes the capability-gated attack-variation action.
|
|
142
134
|
|
|
143
135
|
## Upstream documents outside the mirror set
|
|
144
136
|
|