@maccesar/titools 5.2.4 → 5.3.0
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 +27 -32
- package/agents/ti-pro.md +1 -1
- package/commands/ti-audit.md +1 -1
- package/docs/examples.md +15 -8
- package/package.json +1 -1
- package/skills/ti-synthengine/SKILL.md +62 -92
- package/skills/ti-synthengine/assets/example/README.md +51 -54
- package/skills/ti-synthengine/assets/example/app.js +96 -57
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +39 -40
- package/skills/ti-synthengine/assets/example/lib/soundPreset.js +34 -0
- package/skills/ti-synthengine/assets/index.d.ts +226 -0
- package/skills/ti-synthengine/references/advanced-examples.md +12 -12
- package/skills/ti-synthengine/references/api.md +125 -33
- package/skills/ti-synthengine/references/audio-engine.md +24 -59
- package/skills/ti-synthengine/references/examples.md +64 -7
- package/skills/ti-synthengine/references/guide.md +69 -63
- package/skills/ti-synthengine/references/instruments-and-effects.md +34 -27
- package/skills/ti-synthengine/references/native-variation.md +92 -51
- package/skills/ti-synthengine/references/recipes-interaction.md +26 -19
- package/skills/ti-synthengine/references/recipes.md +19 -15
- package/skills/ti-synthengine/references/recommendations.md +14 -14
- package/skills/ti-synthengine/references/sound-api.md +197 -0
- package/skills/ti-synthengine/references/sound-design.md +11 -11
- package/skills/ti-synthengine/references/source-notes.md +62 -147
- package/skills/ti-synthengine/references/troubleshooting.md +22 -24
- package/skills/ti-synthengine/references/upgrade-2.0.0.md +40 -0
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +0 -162
- package/skills/ti-synthengine/references/audio-pipeline-validation.md +0 -19
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-physical-validation.json +0 -1918
- package/skills/ti-synthengine/references/native-variation-proposal.md +0 -182
- package/skills/ti-synthengine/references/native-variation-validation.md +0 -63
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +0 -1146
- package/skills/ti-synthengine/references/physical-validation.json +0 -1457
- package/skills/ti-synthengine/references/source-snapshot.json +0 -463
- package/skills/ti-synthengine/references/stats-performance.json +0 -2992
- package/skills/ti-synthengine/references/stats-performance.md +0 -82
- package/skills/ti-synthengine/references/studio-validation.json +0 -6202
- package/skills/ti-synthengine/references/unified-bypass-performance.json +0 -22425
- package/skills/ti-synthengine/references/unified-validation.json +0 -314
|
@@ -1,27 +1,66 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine documentation at commit `
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `9d74aa45f84d3f2822d4aab76874efe37d81257f` (2026-11-03). Published module 2.0.0. Upstream `documentation/native-variation.md` SHA-256: `6cfd65b0741dea79d37bb5eee14d1bcb98445ee6b5db14a8c00e3ccfd00a5190`.
|
|
2
2
|
|
|
3
3
|
# Native per-attack variation
|
|
4
4
|
|
|
5
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
6
|
|
|
7
|
-
Android and iOS support this dictionary in
|
|
7
|
+
Android and iOS support this dictionary in TiSynth 2.0.0. The variation data schema is `1`. Store it in `sound.variation`; a pattern step can supply `steps[i].sound.variation`.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
<!-- TOC-START -->
|
|
10
|
+
## Contents
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
- [Reuse a sound with variation](#reuse-a-sound-with-variation)
|
|
13
|
+
- [Select the module version](#select-the-module-version)
|
|
14
|
+
- [Detect support and start playback](#detect-support-and-start-playback)
|
|
15
|
+
- [Supported locations](#supported-locations)
|
|
16
|
+
- [Dictionary reference](#dictionary-reference)
|
|
17
|
+
- [What amount means](#what-amount-means)
|
|
18
|
+
- [Repeated independent effects](#repeated-independent-effects)
|
|
19
|
+
- [Chords and application gain](#chords-and-application-gain)
|
|
20
|
+
- [Sustained voices and explicit live edits](#sustained-voices-and-explicit-live-edits)
|
|
21
|
+
- [Native loops and dictionary inheritance](#native-loops-and-dictionary-inheritance)
|
|
22
|
+
- [Seeds and reproducibility](#seeds-and-reproducibility)
|
|
23
|
+
- [Rejected configurations](#rejected-configurations)
|
|
24
|
+
- [Studio and game integration](#studio-and-game-integration)
|
|
25
|
+
- [Implementation and verification](#implementation-and-verification)
|
|
26
|
+
|
|
27
|
+
<!-- TOC-END -->
|
|
28
|
+
|
|
29
|
+
## Reuse a sound with variation
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
var synth = require('ti.synthengine')
|
|
33
|
+
var bell = synth.createSound({
|
|
34
|
+
sound: {
|
|
35
|
+
waveType: synth.getContract().waveTypes.SINE,
|
|
36
|
+
volume: 0.25, attack: 5, release: 120,
|
|
37
|
+
variation: { amount: 0.6, ranges: { volume: [0.18, 0.25], pitchCents: [-8, 8] } }
|
|
38
|
+
},
|
|
39
|
+
playback: { type: 'tone', notes: ['A4'], duration: 400 }
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
bell.play()
|
|
43
|
+
bell.play({ notes: ['C5'] })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Call playback after engine startup. The detailed native dictionaries below explain source restrictions and inheritance. For Sound patterns, root variation belongs in `sound.variation` and a step replacement belongs in `steps[i].sound.variation`.
|
|
47
|
+
|
|
48
|
+
## Select the module version
|
|
49
|
+
|
|
50
|
+
Install the matching Android and iOS 2.0.0 archives in the app and select them in `tiapp.xml`:
|
|
12
51
|
|
|
13
52
|
```xml
|
|
14
53
|
<modules>
|
|
15
|
-
<module platform="android" version="
|
|
16
|
-
<module platform="iphone" version="
|
|
54
|
+
<module platform="android" version="2.0.0">ti.synthengine</module>
|
|
55
|
+
<module platform="iphone" version="2.0.0">ti.synthengine</module>
|
|
17
56
|
</modules>
|
|
18
57
|
```
|
|
19
58
|
|
|
20
|
-
Rebuild and reinstall the application after replacing native modules. Copying module files alone does not update an installed app. Keep
|
|
59
|
+
Rebuild and reinstall the application after replacing native modules. Copying module files alone does not update an installed app. Keep historical released archives separate. The installed module version is available as `getDefaults().version`.
|
|
21
60
|
|
|
22
61
|
## Detect support and start playback
|
|
23
62
|
|
|
24
|
-
Check both
|
|
63
|
+
Check both capability/schema fields, even when the module version matches the one you expect. Omit the property completely if either field is missing, including when the requested amount is zero.
|
|
25
64
|
|
|
26
65
|
```javascript
|
|
27
66
|
var synth = require('ti.synthengine')
|
|
@@ -35,19 +74,21 @@ synth.start({ maxVoices: 8, maxSequences: 1 })
|
|
|
35
74
|
synth.setVolume({ volume: 0.6 })
|
|
36
75
|
```
|
|
37
76
|
|
|
38
|
-
The
|
|
77
|
+
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
78
|
|
|
40
79
|
## Supported locations
|
|
41
80
|
|
|
42
|
-
| Call or score field
|
|
43
|
-
|
|
|
44
|
-
| `playTone({ variation, ... })`
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
81
|
+
| Call or score field | Behavior |
|
|
82
|
+
| -------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
|
83
|
+
| `playTone({ variation, ... })` | Chooses once for an admitted timed tone |
|
|
84
|
+
| `repeatTone({ variation, intervalMs, ... })` | Retains a stream/counter and chooses once per admitted repeated finite attack |
|
|
85
|
+
| `updateTone({ toneId, variation, ... })` | Replaces the whole future-attack dictionary; amount zero disables; seed change resets the stream |
|
|
86
|
+
| `playChord({ variation, ... })` | Admits the whole chord atomically and chooses independent values for each voice |
|
|
87
|
+
| `noteOn({ variation, ... })` | Chooses at the held voice's attack; sustain and release keep that configuration |
|
|
88
|
+
| `playSequence({ voice: { variation, ... }, ... })` | Supplies the default for sounding steps on every native loop |
|
|
89
|
+
| `steps[i].overrides.variation` | Replaces the entire dictionary inherited from the sequence voice |
|
|
90
|
+
| `updateSequence({ voice, steps, ... })` | Changes future attacks when the revision applies |
|
|
91
|
+
| `updateNote({ variation, ... })` | Rejected; live edits must name the parameters they change |
|
|
51
92
|
|
|
52
93
|
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
94
|
|
|
@@ -64,26 +105,26 @@ var variation = {
|
|
|
64
105
|
}
|
|
65
106
|
```
|
|
66
107
|
|
|
67
|
-
| Field
|
|
68
|
-
|
|
|
69
|
-
| `amount` | `1`
|
|
70
|
-
| `seed`
|
|
71
|
-
| `ranges` | Empty dictionary
|
|
108
|
+
| Field | Default | Accepted values |
|
|
109
|
+
| -------- | --------------------------------------- | ----------------------------------------------------------------------- |
|
|
110
|
+
| `amount` | `1` | Finite number from `0` to `1` |
|
|
111
|
+
| `seed` | Omitted; engine selects a native stream | Integer from `0` to `4294967295`; `0` is an explicit deterministic seed |
|
|
112
|
+
| `ranges` | Empty dictionary | Any subset of the eight range names below |
|
|
72
113
|
|
|
73
114
|
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
115
|
|
|
75
116
|
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
117
|
|
|
77
|
-
| Range
|
|
78
|
-
|
|
|
79
|
-
| `volume`
|
|
80
|
-
| `filterHz`
|
|
81
|
-
| `filterQ`
|
|
82
|
-
| `fmDepth`
|
|
83
|
-
| `noiseRate`
|
|
84
|
-
| `lowPassHz`
|
|
85
|
-
| `pulseWidth` | `0.05` to `0.95`
|
|
86
|
-
| `pitchCents` | `-1200` to `1200` cents
|
|
118
|
+
| Range | Inclusive validation bounds | Required source or setting |
|
|
119
|
+
| ------------ | --------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
120
|
+
| `volume` | `0` to `2` | Any source; existing chord compensation follows selection |
|
|
121
|
+
| `filterHz` | `20` Hz to the active frequency ceiling | Enabled resonant filter: `lowpass`, `highpass` or `bandpass` |
|
|
122
|
+
| `filterQ` | `0.5` to `10` | Enabled resonant filter |
|
|
123
|
+
| `fmDepth` | `0` to `10` | `SINE`, with the normal FM ratio/depth rules |
|
|
124
|
+
| `noiseRate` | `1` Hz to the active frequency ceiling | `NOISE` with authored `noiseRate >= 1` |
|
|
125
|
+
| `lowPassHz` | `20` Hz to the active frequency ceiling | One-pole low-pass already enabled with authored `lowPassHz >= 20` |
|
|
126
|
+
| `pulseWidth` | `0.05` to `0.95` | `SQUARE` |
|
|
127
|
+
| `pitchCents` | `-1200` to `1200` cents | Resulting tuned frequencies and active pitch endpoints must remain within engine limits |
|
|
87
128
|
|
|
88
129
|
`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
130
|
|
|
@@ -112,7 +153,7 @@ Validation checks the complete reachable configuration before admission, includi
|
|
|
112
153
|
|
|
113
154
|
## Repeated independent effects
|
|
114
155
|
|
|
115
|
-
Leave out `seed` to obtain successive native selections. Call `playTone` for each actual
|
|
156
|
+
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.
|
|
116
157
|
|
|
117
158
|
```javascript
|
|
118
159
|
function fireLaser() {
|
|
@@ -131,11 +172,11 @@ function fireLaser() {
|
|
|
131
172
|
fireLaser()
|
|
132
173
|
```
|
|
133
174
|
|
|
134
|
-
The engine samples after
|
|
175
|
+
The engine samples after admitting an attack, before initializing its filters, smoothing controls and trajectories. Rejected attacks consume no selection. Release tails, capacity policy and voice replacement remain unchanged.
|
|
135
176
|
|
|
136
177
|
## Chords and application gain
|
|
137
178
|
|
|
138
|
-
|
|
179
|
+
A chord is published atomically. Each voice's position within the attack determines its independent field samples, so later voices do not reuse the first voice's result. If the chord cannot be admitted, none of its voices starts and no event is consumed.
|
|
139
180
|
|
|
140
181
|
Scale a volume range with the same application gain as the authored volume. Let the engine apply its existing chord compensation once.
|
|
141
182
|
|
|
@@ -171,11 +212,11 @@ if (supportsVariation) held.variation = {
|
|
|
171
212
|
ranges: { fmDepth: [1.1, 1.7], pitchCents: [-10, 10] }
|
|
172
213
|
}
|
|
173
214
|
synth.noteOn(held)
|
|
174
|
-
synth.
|
|
215
|
+
synth.updateNote({ voiceId: 71, volume: 0.12 })
|
|
175
216
|
synth.noteOff({ voiceId: 71 })
|
|
176
217
|
```
|
|
177
218
|
|
|
178
|
-
`
|
|
219
|
+
`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.
|
|
179
220
|
|
|
180
221
|
## Native loops and dictionary inheritance
|
|
181
222
|
|
|
@@ -232,22 +273,22 @@ if (supportsVariation && sequenceId > 0) {
|
|
|
232
273
|
}
|
|
233
274
|
```
|
|
234
275
|
|
|
235
|
-
Stop the owned sequence
|
|
276
|
+
Stop the owned sequence during normal lifecycle cleanup when it is no longer needed. Stopping and starting playback again creates a new stream.
|
|
236
277
|
|
|
237
278
|
## Seeds and reproducibility
|
|
238
279
|
|
|
239
|
-
| Usage
|
|
240
|
-
|
|
|
280
|
+
| Usage | Reproducibility |
|
|
281
|
+
| ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
241
282
|
| 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
|
|
243
|
-
| Seeded native loop
|
|
244
|
-
| Revision with unchanged seed identity
|
|
245
|
-
| Revision that changes the root seed
|
|
246
|
-
| Stop followed by a new sequence
|
|
283
|
+
| Standalone calls without a seed | Use successive native selections |
|
|
284
|
+
| Seeded native loop | Advances its attack counter across loop iterations |
|
|
285
|
+
| Revision with unchanged seed identity | Retains that counter |
|
|
286
|
+
| Revision that changes the root seed | Resets the counter when the revision applies |
|
|
287
|
+
| Stop followed by a new sequence | Starts a new counter |
|
|
247
288
|
|
|
248
289
|
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
290
|
|
|
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
|
|
291
|
+
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.
|
|
251
292
|
|
|
252
293
|
## Rejected configurations
|
|
253
294
|
|
|
@@ -264,7 +305,7 @@ synth.playTone({ variation: {
|
|
|
264
305
|
} })
|
|
265
306
|
|
|
266
307
|
// Live updates do not accept an attack-only dictionary.
|
|
267
|
-
synth.
|
|
308
|
+
synth.updateNote({ voiceId: 71,
|
|
268
309
|
variation: { ranges: { pitchCents: [-10, 10] } } })
|
|
269
310
|
```
|
|
270
311
|
|
|
@@ -274,10 +315,10 @@ Also check pitch corners near 1 Hz or the reported ceiling. A valid base pitch c
|
|
|
274
315
|
|
|
275
316
|
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
317
|
|
|
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
|
|
318
|
+
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. 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
319
|
|
|
279
320
|
## Implementation and verification
|
|
280
321
|
|
|
281
|
-
Both renderers use [`shared/synth_variation.h`](https://github.com/macCesar/TiSynthEngine/blob/
|
|
322
|
+
Both renderers use [`shared/synth_variation.h`](https://github.com/macCesar/TiSynthEngine/blob/9d74aa45f84d3f2822d4aab76874efe37d81257f/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
323
|
|
|
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.
|
|
324
|
+
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](https://github.com/macCesar/TiSynthEngine/blob/9d74aa45f84d3f2822d4aab76874efe37d81257f/documentation/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,10 +1,16 @@
|
|
|
1
1
|
# Instrument, polyphony and expressive recipes
|
|
2
2
|
|
|
3
|
-
These
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
These TiTools recipes use the supported direct interface in 2.0.0 and share
|
|
4
|
+
the running engine and runtime `WAVE` constants from
|
|
5
|
+
[the arcade recipes](recipes.md). Keep one engine owner, check native acceptance
|
|
6
|
+
and follow the [verification checklist](recipes.md#recipe-verification-checklist).
|
|
7
|
+
|
|
8
|
+
The envelope, filter and FM ranges are starting points checked against the
|
|
9
|
+
public contract. They have not been auditioned on a device, so listen before
|
|
10
|
+
shipping.
|
|
11
|
+
|
|
12
|
+
For new reusable instruments, read [Sound definitions and controls](sound-api.md).
|
|
13
|
+
Keep these recipes' gains, pitch arrays, envelopes and ownership when adapting them.
|
|
8
14
|
|
|
9
15
|
```javascript
|
|
10
16
|
const synth = require('ti.synthengine')
|
|
@@ -114,9 +120,8 @@ In a Titanium view:
|
|
|
114
120
|
- call `gesture.finish()` on both `touchend` and `touchcancel`;
|
|
115
121
|
- keep visual pressed-state counts separate if multiple touches are supported.
|
|
116
122
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
sequence and invalidates shared effect histories. Use an owner's
|
|
123
|
+
For a xylophone, let the audio release continue after the finger leaves a bar.
|
|
124
|
+
`stopAll()` is a global panic: it also cancels every native sequence and invalidates shared effect histories. Use an owner's
|
|
120
125
|
`stopSequence({ sequenceId })` when only that sequence should stop.
|
|
121
126
|
|
|
122
127
|
## Four-pad memory cues
|
|
@@ -169,8 +174,8 @@ function playMemoryBank (bankName) {
|
|
|
169
174
|
|
|
170
175
|
### Controlled timbre comparison
|
|
171
176
|
|
|
172
|
-
For an
|
|
173
|
-
|
|
177
|
+
For an A/B test, hold pitches, duration, envelope and pan constant. Change
|
|
178
|
+
only the waveform and its comparison level. Never pass a profile object
|
|
174
179
|
containing labels or other app metadata directly to `playChord()` because its
|
|
175
180
|
options dictionary is closed.
|
|
176
181
|
|
|
@@ -202,8 +207,8 @@ function auditionTimbre (profileName) {
|
|
|
202
207
|
|
|
203
208
|
### Convert MIDI pitch to hertz
|
|
204
209
|
|
|
205
|
-
|
|
206
|
-
|
|
210
|
+
Use the standard A4 = 440 Hz formula to build interval recipes without
|
|
211
|
+
embedding a long frequency table.
|
|
207
212
|
|
|
208
213
|
```javascript
|
|
209
214
|
function midiToHz (midi) {
|
|
@@ -337,7 +342,9 @@ This repeated chord uses one centered native sequence. The one-off layered chord
|
|
|
337
342
|
|
|
338
343
|
### Cancellable repetition
|
|
339
344
|
|
|
340
|
-
|
|
345
|
+
The chord pulse above belongs to one identified sequence owner. Repeated start requests coalesce until the matching `sequenceend` event retires it and frees the slot. When its window closes, stop that sequence ID and cancel pending restarts. The native sequence schedules subsequent chord attacks.
|
|
346
|
+
|
|
347
|
+
For direct repetition of an original finite tone, 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
348
|
|
|
342
349
|
```javascript
|
|
343
350
|
function cleanupAudio () {
|
|
@@ -354,9 +361,9 @@ Call `shutdown()` only from the screen or service that owns the shared engine.
|
|
|
354
361
|
|
|
355
362
|
### Plucked marimba note
|
|
356
363
|
|
|
357
|
-
A short attack
|
|
358
|
-
|
|
359
|
-
needs a decay, and `attack + decay` must fit inside `duration`.
|
|
364
|
+
A short attack followed by decay to a low sustain gives the strike and ring
|
|
365
|
+
of a mallet or plucked string. Keep the release within the available duration.
|
|
366
|
+
A sustain below 1 needs a decay, and `attack + decay` must fit inside `duration`.
|
|
360
367
|
|
|
361
368
|
```javascript
|
|
362
369
|
synth.playTone({
|
|
@@ -410,9 +417,9 @@ synth.playTone({
|
|
|
410
417
|
|
|
411
418
|
### Electric piano
|
|
412
419
|
|
|
413
|
-
Frequency modulation with a 1:1 ratio gives a tine-like tone.
|
|
414
|
-
modulation index
|
|
415
|
-
|
|
420
|
+
Frequency modulation with a 1:1 ratio gives a tine-like tone. Reduce the
|
|
421
|
+
modulation index through `fmDepthEnd` so the note mellows as it rings, as a
|
|
422
|
+
struck tine does. FM needs a sine carrier.
|
|
416
423
|
|
|
417
424
|
```javascript
|
|
418
425
|
synth.playTone({
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Curated sound and interaction recipes
|
|
2
2
|
|
|
3
|
-
These recipes
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
These recipes use the supported direct native interface in `ti.synthengine` 2.0.0
|
|
4
|
+
and earlier 1.x modules. For reusable definitions in new code, use
|
|
5
|
+
[Sound objects](sound-api.md) while preserving how each recipe renders.
|
|
6
|
+
|
|
7
|
+
The examples use public option keys and valid explicit envelopes. They read
|
|
8
|
+
waveforms from the runtime contract instead of hardcoding integers. Install
|
|
9
|
+
matching official Android/iOS packages and follow the
|
|
10
|
+
[installation and lifecycle guide](guide.md) to set up the engine owner.
|
|
8
11
|
|
|
9
12
|
Unless a section says otherwise, the snippets assume one owner has already
|
|
10
13
|
started the engine:
|
|
@@ -15,13 +18,13 @@ const contract = synth.getDefaults()
|
|
|
15
18
|
const WAVE = contract.waveTypes
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
Use the levels as starting points for listening tests, rather than final
|
|
22
|
+
mastering targets. Test on the actual phone speaker and leave headroom for
|
|
23
|
+
overlapping release tails or independent events.
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
[the interaction recipes](recipes-interaction.md).
|
|
24
|
-
|
|
25
|
+
For instrument interactions, layered chords and expressive synthesis, see
|
|
26
|
+
[the interaction recipes](recipes-interaction.md). Listen to those starting
|
|
27
|
+
ranges on the target device before shipping.
|
|
25
28
|
|
|
26
29
|
<!-- TOC-START -->
|
|
27
30
|
## Contents
|
|
@@ -165,8 +168,8 @@ if (!sequenceId) throw new Error('Sequence rejected')
|
|
|
165
168
|
|
|
166
169
|
### Fast four-note arpeggio
|
|
167
170
|
|
|
168
|
-
Very short square notes with a small attack
|
|
169
|
-
edge
|
|
171
|
+
Very short square notes with a small attack keep the hard cartridge-era
|
|
172
|
+
edge and reduce uncontrolled clicks. The 10 ms rests keep the first three
|
|
170
173
|
40 ms notes on a 50 ms grid; the final note rings for 80 ms.
|
|
171
174
|
|
|
172
175
|
```javascript
|
|
@@ -403,8 +406,9 @@ if (!sequenceId) throw new Error('Sequence rejected')
|
|
|
403
406
|
|
|
404
407
|
### Hovering craft
|
|
405
408
|
|
|
406
|
-
A triangle voice with strong sine vibrato creates a
|
|
407
|
-
remains the primary cue
|
|
409
|
+
A triangle voice with strong sine vibrato creates a repeating engine wobble.
|
|
410
|
+
Slow stereo travel adds movement on headphones; pitch remains the primary cue
|
|
411
|
+
on mono speakers.
|
|
408
412
|
|
|
409
413
|
```javascript
|
|
410
414
|
synth.playTone({
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine documentation at commit `
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `9d74aa45f84d3f2822d4aab76874efe37d81257f` (2026-11-03). Published module 2.0.0. Upstream `documentation/recommendations.md` SHA-256: `0e7c55830b05ef21bdd1fd125e7d158ea5b598e02618202e65ddd8b8d2044cab`.
|
|
2
2
|
|
|
3
3
|
# Recommendations
|
|
4
4
|
|
|
5
|
-
The defaults work for ordinary tones and chords.
|
|
5
|
+
The defaults work for ordinary tones and chords. Use the guidance below when an app has dense overlap, user-controlled values, demanding speakers or several audio systems sharing one lifecycle.
|
|
6
6
|
|
|
7
7
|
## Group simultaneous notes
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Declare `playback.type: 'chord'` when pitches form one known simultaneous event. The chord is atomic. Protected profiles normalize per-voice source gain by the square root of the event voice count; `raw` bypasses that normalization. Separate tone executions suit independent effects, but their start times and gain are independent too.
|
|
10
10
|
|
|
11
|
-
For keys or pads
|
|
11
|
+
For freely triggered keys or pads, keep each event around 0.4 to 0.7 until you have listened to the worst overlap. If every event needs gain above 1, consider `balanced` for the session before raising individual notes further.
|
|
12
12
|
|
|
13
13
|
## Choose the mixing profile once
|
|
14
14
|
|
|
@@ -25,7 +25,7 @@ If a phone buzzes under `speakerSafe`, lower the event volume or raise the pitch
|
|
|
25
25
|
|
|
26
26
|
## Size `maxVoices` for real overlap
|
|
27
27
|
|
|
28
|
-
`maxVoices`
|
|
28
|
+
`maxVoices` sets voice capacity without changing event gain.
|
|
29
29
|
|
|
30
30
|
| App behavior | Starting value |
|
|
31
31
|
| ---------------------------------- | -------------: |
|
|
@@ -34,7 +34,7 @@ If a phone buzzes under `speakerSafe`, lower the event volume or raise the pitch
|
|
|
34
34
|
| Chords, keys or long release tails | 16 to 24 |
|
|
35
35
|
| Explicit stress testing | 32 |
|
|
36
36
|
|
|
37
|
-
Count release tails
|
|
37
|
+
Count release tails as well as buttons pressed at the same instant. Replacement first selects the lowest eligible priority; higher-priority voices are protected. Among that priority, timed-only events steal the slot with the least time remaining; sustained/transport voices prefer a releasing slot, then the oldest attack. The short fade avoids a hard click; the stolen note still ends early.
|
|
38
38
|
|
|
39
39
|
## Keep one lifecycle owner
|
|
40
40
|
|
|
@@ -67,7 +67,7 @@ The resonant filter, FM and stacked detuned voices change level and spectrum mor
|
|
|
67
67
|
- A resonance that keeps reaching the limiter makes the whole mix pump. Lower the volume or the Q before reaching for a different profile.
|
|
68
68
|
- FM is not band-limited. A high carrier with a large `fmDepth` and `fmRatio` aliases. Keep carriers of bell-like sounds around 500 Hz, or lower the index.
|
|
69
69
|
- A detuned stack is one voice per copy. Count them when choosing `maxVoices`. Protected profiles divide per-voice source gain by the square root of the event voice count; `raw` does not. Correlated or phase-aligned voices can still reinforce each other, so this is not a guarantee of constant loudness or peaks.
|
|
70
|
-
- `
|
|
70
|
+
- A pattern control emits `end` on completion or stopping. Put contiguous notes into one pattern when sample timing matters; the event crosses JavaScript delivery latency.
|
|
71
71
|
|
|
72
72
|
## Validate values at the app boundary
|
|
73
73
|
|
|
@@ -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 each method's documented return value during development
|
|
90
|
+
Check each method's documented return value during development. Sound playback returns finite acceptance, a playback control, or `false` on operational rejection; invalid Sound configurations throw. Direct native methods keep their documented Boolean or identifier returns. 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. A silent native log does not establish acceptance.
|
|
91
91
|
|
|
92
92
|
## Test the intended sound
|
|
93
93
|
|
|
@@ -103,7 +103,7 @@ Exercise the real worst case:
|
|
|
103
103
|
- a route change while a note is playing;
|
|
104
104
|
- screen off, interruption and background transitions if the product depends on them.
|
|
105
105
|
|
|
106
|
-
An underrun counter
|
|
106
|
+
An underrun counter describes transport; it cannot tell you whether a waveform, pitch or mix is pleasant.
|
|
107
107
|
|
|
108
108
|
## Binaural placement
|
|
109
109
|
|
|
@@ -111,13 +111,13 @@ Use the [integration guide](guide.md#integrate-binaural-in-a-2d-app) to convert
|
|
|
111
111
|
|
|
112
112
|
## Known limits
|
|
113
113
|
|
|
114
|
-
-
|
|
114
|
+
- Keep the control returned by `sound.noteOn()` or held `play()` for release and compatible live updates. Finite tones and chords return no individual control.
|
|
115
115
|
- Timed tones and chords last at most 60 seconds including release. Sequence intervals and explicit note lengths use the sequence unit, at most 300 beats or 60,000 ms; sequence ADSR remains in milliseconds and release may extend beyond the interval. Sustained `noteOn` voices last until release, replacement or shutdown.
|
|
116
116
|
- The module synthesizes waveforms and does not play audio files.
|
|
117
117
|
- One native transport supports up to four independent sequences, preallocated by `maxSequences`. All share at most 32 voices.
|
|
118
|
-
- A single tone or chord has no completion event.
|
|
118
|
+
- A single finite tone or chord has no completion event. A pattern control emits `end` with its completed/stopped reason.
|
|
119
119
|
- Voice capacity and mixing profile remain fixed until shutdown.
|
|
120
|
-
-
|
|
120
|
+
- Patterns run on the sample clock in beats or milliseconds. Root and step `interval` control onset spacing; `length` independently controls note duration.
|
|
121
121
|
- `lowPassHz` is a plain one-pole filter. The resonant filter has fixed 12 dB per octave and one exponential sweep; there is no filter envelope.
|
|
122
122
|
- FM has one modulator and works only on `SINE`.
|
|
123
123
|
- Binaural positions occupy a front plane. Spatial held voices support smooth live movement; there is no rear positioning, distance attenuation or head tracking. Output is two channels, without a multichannel 2.1/5.1 routing API.
|
|
@@ -128,10 +128,10 @@ Use a sample player or a dedicated music engine when a product needs streaming,
|
|
|
128
128
|
|
|
129
129
|
## Use effects and held voices deliberately
|
|
130
130
|
|
|
131
|
-
Use `noteOn` for a sound whose duration follows interaction, such as a key, motor or beam; release its
|
|
131
|
+
Use `sound.noteOn()` for a sound whose duration follows interaction, such as a key, motor or beam; release its note control on touch end, cancellation, pause or scene teardown. Use a pattern definition for a finite musical cue or repeating grid. The native engine sustains notes and schedules attacks. Both share the same 32-voice maximum with collision and interface tones; held notes can be stolen, and updates to a stolen note are accepted as no-ops. Optional priority 0-3 affects stealing without reserving voices. There is no voice-finished event.
|
|
132
132
|
|
|
133
133
|
Keep chorus/delay/reverb mixes modest when dialogue or short game feedback must remain distinct. By default they process all four categories, so enabling reverb for a victory chord also affects simultaneous shots and collisions. Select categories with `applyTo` and assign each voice a `bus`, or use explicit `effectSend` values to choose the processed share. All selected voices share one effects chain. Disabling effects lets tails drain for at most 30 seconds; use panic when immediate history invalidation is required. Start with saturation disabled and compare creative drive at matched levels. Do not use saturation as an automatic substitute for the final limiter.
|
|
134
134
|
|
|
135
135
|
Fixed input/event gain does not decrease when unrelated voices start. If level dips, inspect `maxLimiterReductionDb` and `preLimiterPeakDb`, then consider same-pitch phase cancellation or the device's speaker protection. Eight coherent independent voices can still exceed the limiter threshold; module capacity is not a clean-mix loudness guarantee. See [measurement example](advanced-examples.md#measure-peaks-and-limiter-reduction).
|
|
136
136
|
|
|
137
|
-
Measure the complete workload on each target device, including held voices, binaural, LFOs, all enabled effects and tails. The [historical statistics benchmark](stats-performance.md) predates these features; the [current technical report](studio-validation.json) includes an unmet full-chain host deadline criterion. Neither is a guarantee of game-frame rate, physical audio deadlines, heat or battery life.
|
|
137
|
+
Measure the complete workload on each target device, including held voices, binaural, LFOs, all enabled effects and tails. The [historical statistics benchmark](https://github.com/macCesar/TiSynthEngine/blob/9d74aa45f84d3f2822d4aab76874efe37d81257f/documentation/stats-performance.md) predates these features; the [current technical report](https://github.com/macCesar/TiSynthEngine/blob/9d74aa45f84d3f2822d4aab76874efe37d81257f/docs/validation/studio-validation.json) includes an unmet full-chain host deadline criterion. Neither is a guarantee of game-frame rate, physical audio deadlines, heat or battery life.
|