@maccesar/titools 5.2.0 → 5.2.1

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 (78) hide show
  1. package/CONTRIBUTING.md +69 -0
  2. package/README.md +80 -72
  3. package/agents/ti-pro.md +1 -1
  4. package/commands/ti-audit.md +2 -2
  5. package/docs/examples.md +91 -0
  6. package/docs/knowledge-index.md +57 -0
  7. package/lib/downloader.js +1 -1
  8. package/package.json +4 -2
  9. package/skills/purgetss/references/multi-density-images.md +1 -1
  10. package/skills/ti-admob-unified/SKILL.md +21 -7
  11. package/skills/ti-admob-unified/assets/example/app.js +229 -25
  12. package/skills/ti-admob-unified/assets/example/lib/ads.js +30 -2
  13. package/skills/ti-admob-unified/assets/example/library-usage.js +33 -1
  14. package/skills/ti-admob-unified/references/api-reference.md +39 -16
  15. package/skills/ti-admob-unified/references/banners.md +9 -9
  16. package/skills/ti-admob-unified/references/contributing.md +14 -9
  17. package/skills/ti-admob-unified/references/dependencies.md +31 -25
  18. package/skills/ti-admob-unified/references/fullscreen.md +43 -28
  19. package/skills/ti-admob-unified/references/game-monetization.md +138 -7
  20. package/skills/ti-admob-unified/references/getting-started.md +75 -31
  21. package/skills/ti-admob-unified/references/index.md +5 -2
  22. package/skills/ti-admob-unified/references/integration.md +8 -6
  23. package/skills/ti-admob-unified/references/library.md +41 -10
  24. package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
  25. package/skills/ti-admob-unified/references/mediation.md +22 -9
  26. package/skills/ti-admob-unified/references/migration.md +11 -7
  27. package/skills/ti-admob-unified/references/module-changelog.md +41 -11
  28. package/skills/ti-admob-unified/references/privacy.md +35 -8
  29. package/skills/ti-admob-unified/references/production.md +67 -0
  30. package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
  31. package/skills/ti-admob-unified/references/roadmap.md +28 -31
  32. package/skills/ti-admob-unified/references/source-notes.md +51 -4
  33. package/skills/ti-admob-unified/references/source-snapshot.json +249 -45
  34. package/skills/ti-admob-unified/references/third-party-notices.md +4 -4
  35. package/skills/ti-game/SKILL.md +23 -6
  36. package/skills/ti-game/references/api.md +34 -30
  37. package/skills/ti-game/references/debugging-performance.md +18 -9
  38. package/skills/ti-game/references/examples.md +103 -20
  39. package/skills/ti-game/references/input.md +101 -0
  40. package/skills/ti-game/references/patterns.md +39 -15
  41. package/skills/ti-game/references/physics.md +80 -52
  42. package/skills/ti-game/references/project-setup.md +118 -56
  43. package/skills/ti-game/references/recipes-action.md +107 -8
  44. package/skills/ti-game/references/recipes-adventure-effects.md +41 -9
  45. package/skills/ti-game/references/roadmap.md +10 -3
  46. package/skills/ti-game/references/source-notes.md +81 -0
  47. package/skills/ti-game/references/tilemaps.md +12 -4
  48. package/skills/ti-synthengine/SKILL.md +81 -95
  49. package/skills/ti-synthengine/assets/example/README.md +84 -0
  50. package/skills/ti-synthengine/assets/example/app.js +1178 -0
  51. package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
  52. package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
  53. package/skills/ti-synthengine/references/advanced-examples.md +174 -0
  54. package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
  55. package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
  56. package/skills/ti-synthengine/references/api.md +434 -380
  57. package/skills/ti-synthengine/references/audio-engine.md +157 -230
  58. package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
  59. package/skills/ti-synthengine/references/examples.md +123 -253
  60. package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
  61. package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
  62. package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
  63. package/skills/ti-synthengine/references/guide.md +188 -156
  64. package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
  65. package/skills/ti-synthengine/references/performance-review.json +1146 -0
  66. package/skills/ti-synthengine/references/physical-validation.json +1457 -0
  67. package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
  68. package/skills/ti-synthengine/references/recipes.md +423 -599
  69. package/skills/ti-synthengine/references/recommendations.md +63 -113
  70. package/skills/ti-synthengine/references/sound-design.md +134 -258
  71. package/skills/ti-synthengine/references/source-notes.md +93 -105
  72. package/skills/ti-synthengine/references/source-snapshot.json +379 -32
  73. package/skills/ti-synthengine/references/stats-performance.md +64 -20
  74. package/skills/ti-synthengine/references/studio-validation.json +6202 -0
  75. package/skills/ti-synthengine/references/troubleshooting.md +109 -121
  76. package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
  77. package/skills/ti-synthengine/references/unified-validation.json +314 -0
  78. package/AGENTS-VERCEL-RESEARCH.md +0 -175
@@ -1,9 +1,12 @@
1
- > Source snapshot: official TiSynthEngine 1.0.0 beta documentation from the reviewed working tree based on `95901c98af5353b86d9dc7c60b29ab9186b038cd` (2026-10-14), including uncommitted statistics changes. Upstream `documentation/examples.md` SHA-256: `53a8bd95215cc24a35776edb8686fa0d60c5399be0b4ff2685eee1563738f6cb`.
1
+ > Source snapshot: official TiSynthEngine 1.0.0 documentation at release source commit `e7cbbe8a6f9ab38088fd8029c7b43cef6501101f` (2026-10-24). Upstream `documentation/examples.md` SHA-256: `48104b9d413050d439ee605c92a2456a50b42c4c80e2d7c2c05706eecc7db215`.
2
2
 
3
3
  # Examples
4
4
 
5
- Every snippet on this page uses the TiSynthEngine 1.0.0 contract. Unless a
6
- snippet includes startup code, it assumes the engine is already running.
5
+ Every snippet on this page uses the TiSynthEngine 1.0.0 contract. Unless a snippet includes startup code, it assumes the engine is already running.
6
+
7
+ Additional [curated recipes](recipes.md) provide application sound and interaction variants.
8
+
9
+ For interactive instruments and native processing, begin with [held motors](advanced-examples.md#sustained-game-motor), [native sequences](advanced-examples.md#native-sequence-with-chords-and-edits), [master processing](advanced-examples.md#chorus-delay-reverb-and-creative-saturation) or [two LFOs](advanced-examples.md#two-voice-lfos). Use matching official packages and check method availability before enabling a feature.
7
10
 
8
11
  ```javascript
9
12
  var synth = require('ti.synthengine')
@@ -40,7 +43,7 @@ if (contract.apiVersion !== '1.0.0') {
40
43
  - [Wah and filtered laser](#wah-and-filtered-laser)
41
44
  - [Kick and hi-hat in one pattern](#kick-and-hi-hat-in-one-pattern)
42
45
  - [Detuned chords](#detuned-chords)
43
- - [Chain patterns with `patternend`](#chain-patterns-with-patternend)
46
+ - [Chain sequences with `sequenceend`](#chain-sequences-with-sequenceend)
44
47
  - [Rules across keys](#rules-across-keys)
45
48
  - [Speaker-safe startup](#speaker-safe-startup)
46
49
  - [Validation at an input boundary](#validation-at-an-input-boundary)
@@ -60,10 +63,12 @@ function onPostLayout () {
60
63
  timer = setTimeout(function () {
61
64
  timer = null
62
65
  win.removeEventListener('postlayout', onPostLayout)
63
- started = synth.startEngine({
66
+ started = synth.start({
64
67
  maxVoices: 16,
65
- mixingProfile: 'conservative'
68
+ mixingProfile: 'conservative',
69
+ audioSession: 'ambient'
66
70
  })
71
+ if (!started) Ti.API.warn('Audio startup failed; keep playback controls disabled')
67
72
  }, 250)
68
73
  }
69
74
 
@@ -78,26 +83,21 @@ win.addEventListener('close', cleanup)
78
83
  win.open()
79
84
  ```
80
85
 
86
+ The startup example uses `ambient` for game/UI feedback. On iOS it respects silent mode and mixes with other apps. The module default is `playback`; Android validates either value without changing its output. Shut down before changing the policy. Use the [interactive session controls](../assets/example/README.md#audio-session) to compare them.
87
+
81
88
  ## Built-in waveforms
82
89
 
83
90
  ```javascript
84
91
  var names = ['SINE', 'SQUARE', 'SAWTOOTH', 'TRIANGLE', 'NOISE']
85
-
86
- names.forEach(function (name, index) {
87
- setTimeout(function () {
88
- synth.playTone({
89
- note: 'A4',
90
- duration: 400,
91
- attack: 8,
92
- release: 100,
93
- volume: 0.6,
94
- waveType: WAVE[name]
95
- })
96
- }, index * 500)
92
+ synth.playSequence({ timeUnit: 'ms', stepDuration: 500,
93
+ voice: { attack: 8, release: 100, volume: 0.6 },
94
+ steps: names.map(function (name) {
95
+ return { notes: [69], length: 300, overrides: { waveType: WAVE[name] } }
96
+ })
97
97
  })
98
98
  ```
99
99
 
100
- Noise ignores pitch, but `note` keeps the event shape valid.
100
+ Noise ignores pitch; each sounding step still supplies one pitch array.
101
101
 
102
102
  ## Variable-rate filtered noise
103
103
 
@@ -116,8 +116,7 @@ synth.playTone({
116
116
  })
117
117
  ```
118
118
 
119
- `noiseRate` starts at 12 kHz and falls to 600 Hz. `lowPassHz` removes high
120
- frequency energy after the 8-bit source quantizer.
119
+ `noiseRate` starts at 12 kHz and falls to 600 Hz. `lowPassHz` removes high frequency energy after the 8-bit source quantizer.
121
120
 
122
121
  ## Pulse-width sweep
123
122
 
@@ -151,8 +150,7 @@ synth.playTone({
151
150
  })
152
151
  ```
153
152
 
154
- The table repeats as a stepped cycle. `bitDepth: 8` quantizes each table value
155
- before filtering and the amplitude envelope.
153
+ The table repeats as a stepped cycle. `bitDepth: 8` quantizes each table value before filtering and the amplitude envelope.
156
154
 
157
155
  ## Fixed controls on a chord
158
156
 
@@ -171,48 +169,25 @@ synth.playChord({
171
169
  })
172
170
  ```
173
171
 
174
- Chord voices share fixed shaping controls. Sweep fields such as
175
- `pulseWidthEnd` and `noiseRateEnd` are not accepted by `playChord()`.
172
+ Chord voices share fixed shaping controls. Sweep fields such as `pulseWidthEnd` and `noiseRateEnd` are not accepted by `playChord()`.
176
173
 
177
174
  ## Wave-shaping pattern overrides
178
175
 
179
176
  ```javascript
180
- synth.playPattern({
177
+ synth.playSequence({
178
+ timeUnit: 'ms',
181
179
  stepDuration: 210,
182
- attack: 2,
183
- release: 80,
184
- volume: 0.48,
185
- waveType: WAVE.NOISE,
186
- noiseRate: 12000,
187
- lowPassHz: 2600,
188
- bitDepth: 8,
180
+ voice: { attack: 2, release: 80, volume: 0.48, waveType: WAVE.NOISE, noiseRate: 12000, lowPassHz: 2600, bitDepth: 8 },
189
181
  steps: [
190
- { frequency: 120, duration: 180, release: 150, noiseRateEnd: 800 },
182
+ { frequencies: [120], duration: 180, overrides: { release: 150, noiseRateEnd: 800 } },
191
183
  { rest: true, duration: 100 },
192
- {
193
- note: 'A3',
194
- duration: 300,
195
- release: 120,
196
- waveType: WAVE.SQUARE,
197
- pulseWidth: 0.1,
198
- pulseWidthEnd: 0.82,
199
- lowPassHz: 0,
200
- bitDepth: 0
201
- },
202
- {
203
- note: 'D4',
204
- duration: 360,
205
- release: 160,
206
- waveType: WAVE.WAVETABLE,
207
- waveTable: [-1, -0.7, -0.1, 0.8, 1, 0.35, -0.25, -0.55]
208
- }
184
+ { notes: [57], duration: 300, overrides: { release: 120, waveType: WAVE.SQUARE, pulseWidth: 0.1, pulseWidthEnd: 0.82, lowPassHz: 0, bitDepth: 0 } },
185
+ { notes: [62], duration: 360, overrides: { release: 160, waveType: WAVE.WAVETABLE, waveTable: [-1, -0.7, -0.1, 0.8, 1, 0.35, -0.25, -0.55] } }
209
186
  ]
210
187
  })
211
188
  ```
212
189
 
213
- Pattern roots provide inherited values. Each sounding step can override the
214
- source and any shaping control; a `WAVETABLE` step needs a local or inherited
215
- `waveTable`.
190
+ Pattern roots provide inherited values. Each sounding step can override the source and any shaping control; a `WAVETABLE` step needs a local or inherited `waveTable`.
216
191
 
217
192
  ## Pitch sweep and pan movement
218
193
 
@@ -230,8 +205,7 @@ synth.playTone({
230
205
  })
231
206
  ```
232
207
 
233
- Set `frequencyEnd: 0` to keep the starting pitch. Set `panEnd` equal to `pan`
234
- when no stereo movement is wanted.
208
+ Set `frequencyEnd: 0` to keep the starting pitch. Set `panEnd` equal to `pan` when no stereo movement is wanted.
235
209
 
236
210
  ## Vibrato
237
211
 
@@ -248,8 +222,7 @@ synth.playTone({
248
222
  })
249
223
  ```
250
224
 
251
- `lfoFreq` and `lfoDepth` are both in hertz. A depth of 6 Hz is gentle around
252
- E4; use your ears when moving the same setting to a much lower note.
225
+ `lfoFreq` and `lfoDepth` are both in hertz. A depth of 6 Hz is gentle around E4; use your ears when moving the same setting to a much lower note.
253
226
 
254
227
  ## Chord with an even spread
255
228
 
@@ -279,79 +252,63 @@ synth.playChord({
279
252
  })
280
253
  ```
281
254
 
282
- Use `playChord` for a known simultaneous group. Launching four separate tones
283
- is not atomic and gives each one full event gain.
255
+ Use `playChord` for a known simultaneous group. Launching four separate tones is not atomic and gives each one full event gain.
284
256
 
285
257
  ## Tempo, note values and swing
286
258
 
287
259
  ```javascript
288
- synth.playPattern({
289
- bpm: 132,
260
+ synth.setTempo({ bpm: 132 })
261
+ synth.playSequence({
262
+ timeUnit: 'beats',
263
+ stepDuration: 0.5,
290
264
  swing: 0.12,
291
- noteValue: '8n',
292
- attack: 4,
293
- release: 55,
294
- volume: 0.55,
295
- waveType: WAVE.SQUARE,
265
+ voice: { attack: 4, release: 55, volume: 0.55, waveType: WAVE.SQUARE },
296
266
  steps: [
297
- { note: 'C4' },
298
- { note: 'E4' },
267
+ { notes: [60] },
268
+ { notes: [64] },
299
269
  { rest: true },
300
- { note: 'G4' },
301
- { note: 'C5', noteValue: '4n' }
270
+ { notes: [67] },
271
+ { notes: [72], duration: 1 }
302
272
  ]
303
273
  })
304
274
  ```
305
275
 
306
- Swing offsets scheduled starts without changing voice durations: positive swing delays even steps and advances odd steps (zero-based); negative swing reverses those offsets. Each offset is the swing value times that step's duration, and a start before the call time is clamped to zero. This can overlap voices. Keep it subtle unless the lurch is the intended effect.
276
+ Swing adjusts pairs of intervals by `swing * min(A, B)`: the first grows and the second shrinks by that amount, preserving the pair total. Negative values reverse the adjustment. The default is 0 and the range is −0.75…0.75.
307
277
 
308
278
  ## Millisecond pattern
309
279
 
310
280
  ```javascript
311
- synth.playPattern({
312
- pan: -0.4,
313
- panEnd: 0.4,
314
- waveType: WAVE.TRIANGLE,
281
+ synth.playSequence({
282
+ timeUnit: 'ms',
283
+ stepDuration: 100,
284
+ voice: { pan: -0.4, panEnd: 0.4, waveType: WAVE.TRIANGLE },
315
285
  steps: [
316
- { note: 'C5', duration: 90 },
286
+ { notes: [72], duration: 90 },
317
287
  { rest: true, duration: 20 },
318
- { note: 'D5', duration: 90 },
288
+ { notes: [74], duration: 90 },
319
289
  { rest: true, duration: 20 },
320
- { note: 'E5', duration: 90 },
290
+ { notes: [76], duration: 90 },
321
291
  { rest: true, duration: 20 },
322
- { note: 'G5', duration: 180 }
292
+ { notes: [79], duration: 180 }
323
293
  ]
324
294
  })
325
295
  ```
326
296
 
327
- Each `duration` sets both the voice length and the advance to the next step.
328
- The 90 ms tones followed by 20 ms rests put the first four notes 110 ms apart;
329
- the last tone lasts 180 ms. Rests provide actual silence. `release` only fades
330
- within a sounding step, not a separate gate. The pattern root does not accept
331
- `duration`, and `stepDuration` is only a fallback when a step has no timing.
297
+ Each `duration` sets the interval until the next step. The 90 ms intervals followed by 20 ms rests place attacks 110 ms apart; a release can overlap a rest. Gate defaults to 0.8. Use `length` to choose note duration independently, while ADSR remains milliseconds.
332
298
 
333
299
  ## Step overrides
334
300
 
335
301
  ```javascript
336
- synth.playPattern({
337
- bpm: 100,
338
- noteValue: '8n',
339
- attack: 10,
340
- release: 90,
341
- volume: 0.5,
342
- waveType: WAVE.SINE,
302
+ synth.setTempo({ bpm: 100 })
303
+ synth.playSequence({
304
+ timeUnit: 'beats',
305
+ stepDuration: 0.5,
306
+ voice: { attack: 10, release: 90, volume: 0.5, waveType: WAVE.SINE },
343
307
  steps: [
344
- { note: 'C4' },
345
- {
346
- note: 'E4',
347
- waveType: WAVE.SAWTOOTH,
348
- pan: -0.7,
349
- panEnd: 0.7,
350
- lfoFreq: 7,
351
- lfoDepth: 10
352
- },
308
+ { notes: [60] },
309
+ { notes: [64], overrides: { waveType: WAVE.SAWTOOTH, pan: -0.7, panEnd: 0.7, lfoFreq: 7, lfoDepth: 10 } },
353
310
  { rest: true },
354
- { note: 'G4', volume: 0.75, noteValue: '4n' }
311
+ { notes: [67], duration: 1, overrides: { volume: 0.75 } }
355
312
  ]
356
313
  })
357
314
  ```
@@ -359,51 +316,37 @@ synth.playPattern({
359
316
  ## Noise percussion
360
317
 
361
318
  ```javascript
362
- synth.playPattern({
363
- bpm: 116,
364
- noteValue: '16n',
365
- volume: 0.32,
366
- waveType: WAVE.NOISE,
319
+ synth.setTempo({ bpm: 116 })
320
+ synth.playSequence({
321
+ timeUnit: 'beats',
322
+ stepDuration: 0.25,
323
+ voice: { volume: 0.32, waveType: WAVE.NOISE },
367
324
  steps: [
368
- { frequency: 200, attack: 1, decay: 70, sustain: 0, release: 60 },
325
+ { frequencies: [200], overrides: { attack: 1, decay: 70, sustain: 0, release: 60 } },
369
326
  { rest: true },
370
- { frequency: 200, attack: 1, decay: 32, sustain: 0, release: 25 },
371
- { frequency: 200, attack: 1, decay: 32, sustain: 0, release: 25 },
327
+ { frequencies: [200], overrides: { attack: 1, decay: 32, sustain: 0, release: 25 } },
328
+ { frequencies: [200], overrides: { attack: 1, decay: 32, sustain: 0, release: 25 } },
372
329
  { rest: true },
373
- { frequency: 200, attack: 1, decay: 52, sustain: 0, release: 44 }
330
+ { frequencies: [200], overrides: { attack: 1, decay: 52, sustain: 0, release: 44 } }
374
331
  ]
375
332
  })
376
333
  ```
377
334
 
378
- The steps retain the sixteenth-note grid. Short decay with `sustain: 0` makes
379
- each hit settle to silence before its step ends, without changing that grid.
380
- Every sounding step needs `frequency` or `note`, including a noise step. Do not
381
- write a primitive step such as `200`; primitive steps return
382
- `invalid_step_type`.
335
+ The steps retain the sixteenth-note grid. Short decay with `sustain: 0` makes each hit settle to silence before its step ends, without changing that grid. Every sounding step needs `frequencies` or `notes`, including a noise step. Do not write a primitive step such as `200`; primitive steps reject the complete publication.
383
336
 
384
337
  ## Xylophone sketch
385
338
 
386
339
  ```javascript
387
- function playBar (note, pan) {
388
- return synth.playTone({
389
- note: note,
390
- duration: 520,
391
- attack: 2,
392
- release: 430,
393
- volume: 0.62,
394
- waveType: WAVE.TRIANGLE,
395
- pan: pan
396
- })
397
- }
398
-
399
- playBar('C5', -0.7)
400
- setTimeout(function () { playBar('E5', -0.2) }, 130)
401
- setTimeout(function () { playBar('G5', 0.3) }, 260)
402
- setTimeout(function () { playBar('C6', 0.75) }, 390)
340
+ synth.playSequence({ timeUnit: 'ms', stepDuration: 130,
341
+ voice: { attack: 2, release: 430, volume: 0.62, waveType: WAVE.TRIANGLE },
342
+ steps: [{ notes: [72], length: 90, overrides: { pan: -0.7 } },
343
+ { notes: [76], length: 90, overrides: { pan: -0.2 } },
344
+ { notes: [79], length: 90, overrides: { pan: 0.3 } },
345
+ { notes: [84], length: 90, overrides: { pan: 0.75 } }]
346
+ })
403
347
  ```
404
348
 
405
- These are deliberately independent strikes. Their releases overlap, so leave
406
- headroom in `volume` and choose enough `maxVoices` for the tails.
349
+ Native attacks are 130 ms apart. Each note releases after 90 ms and its 430 ms tail overlaps later strikes. Leave headroom and voice capacity for those tails.
407
350
 
408
351
  ## Simon-style pads
409
352
 
@@ -436,9 +379,7 @@ function playAllPads () {
436
379
 
437
380
  ## Pluck and electric piano
438
381
 
439
- `decay` and `sustain` turn a flat tone into an instrument. The electric piano
440
- adds an FM index that falls with the level, so the attack is bright and the
441
- tail is mellow.
382
+ `decay` and `sustain` shape the tone into a plucked or struck sound. The electric piano also lowers the FM index as the level falls, giving it a bright attack and a mellow tail.
442
383
 
443
384
  ```javascript
444
385
  function pluck (note) {
@@ -489,94 +430,45 @@ synth.playTone({
489
430
  })
490
431
  ```
491
432
 
492
- A fractional `fmRatio` puts the overtones between harmonics, which reads as
493
- metal. `fmDepthEnd: 0` lets the brightness die away completely.
433
+ A fractional `fmRatio` puts the overtones between harmonics, which reads as metal. `fmDepthEnd: 0` lets the brightness die away completely.
494
434
 
495
435
  ## Wah and filtered laser
496
436
 
497
437
  ```javascript
498
- synth.playTone({
499
- note: 'A2',
500
- duration: 1000,
501
- attack: 10,
502
- release: 200,
503
- volume: 0.5,
504
- waveType: WAVE.SAWTOOTH,
505
- filterType: 'bandpass',
506
- filterHz: 350,
507
- filterHzEnd: 2400,
508
- filterQ: 5
438
+ synth.playSequence({ timeUnit: 'ms',
439
+ voice: { waveType: WAVE.SAWTOOTH },
440
+ steps: [{ notes: [45], duration: 1300, length: 800,
441
+ overrides: { attack: 10, release: 200, volume: 0.5,
442
+ filterType: 'bandpass', filterHz: 350, filterHzEnd: 2400, filterQ: 5 } },
443
+ { frequencies: [1600], duration: 340, length: 250,
444
+ overrides: { frequencyEnd: 160, attack: 2, release: 90, volume: 0.4,
445
+ filterType: 'lowpass', filterHz: 7000, filterHzEnd: 500, filterQ: 4 } }]
509
446
  })
510
-
511
- setTimeout(function () {
512
- synth.playTone({
513
- frequency: 1600,
514
- frequencyEnd: 160,
515
- duration: 340,
516
- attack: 2,
517
- release: 90,
518
- volume: 0.4,
519
- waveType: WAVE.SAWTOOTH,
520
- filterType: 'lowpass',
521
- filterHz: 7000,
522
- filterHzEnd: 500,
523
- filterQ: 4
524
- })
525
- }, 1300)
526
447
  ```
527
448
 
528
- The cutoff sweep spans the whole event, so a longer note sweeps more slowly.
449
+ Each sweep reaches its destination at `length`, then holds there through release. Intervals and note lengths remain fixed in milliseconds when the global tempo changes.
529
450
 
530
451
  ## Kick and hi-hat in one pattern
531
452
 
532
453
  Each step can carry its own envelope and filter.
533
454
 
534
455
  ```javascript
535
- synth.playPattern({
536
- bpm: 110,
537
- noteValue: '8n',
456
+ synth.setTempo({ bpm: 110 })
457
+ synth.playSequence({
458
+ timeUnit: 'beats',
459
+ stepDuration: 0.5,
538
460
  steps: [
539
- {
540
- frequency: 150,
541
- frequencyEnd: 42,
542
- noteValue: '4n',
543
- attack: 1,
544
- decay: 140,
545
- sustain: 0.3,
546
- release: 120,
547
- volume: 0.85
548
- },
549
- {
550
- frequency: 440,
551
- waveType: WAVE.NOISE,
552
- attack: 1,
553
- decay: 30,
554
- sustain: 0.25,
555
- release: 45,
556
- volume: 0.45,
557
- filterType: 'highpass',
558
- filterHz: 7000
559
- },
461
+ { frequencies: [150], duration: 1, overrides: { frequencyEnd: 42, attack: 1, decay: 140, sustain: 0.3, release: 120, volume: 0.85 } },
462
+ { frequencies: [440], overrides: { waveType: WAVE.NOISE, attack: 1, decay: 30, sustain: 0.25, release: 45, volume: 0.45, filterType: 'highpass', filterHz: 7000 } },
560
463
  { rest: true },
561
- {
562
- frequency: 440,
563
- waveType: WAVE.NOISE,
564
- attack: 1,
565
- decay: 30,
566
- sustain: 0.25,
567
- release: 45,
568
- volume: 0.45,
569
- filterType: 'highpass',
570
- filterHz: 7000
571
- }
464
+ { frequencies: [440], overrides: { waveType: WAVE.NOISE, attack: 1, decay: 30, sustain: 0.25, release: 45, volume: 0.45, filterType: 'highpass', filterHz: 7000 } }
572
465
  ]
573
466
  })
574
467
  ```
575
468
 
576
469
  ## Detuned chords
577
470
 
578
- Repeat a pitch with small offsets in `detunes` to make it beat and widen. Two
579
- copies 12 cents apart at 440 Hz beat about three times a second.
471
+ Repeat a pitch with small offsets in `detunes` to make it beat and widen. Two copies 12 cents apart at 440 Hz beat about three times a second.
580
472
 
581
473
  ```javascript
582
474
  synth.playChord({
@@ -589,8 +481,8 @@ synth.playChord({
589
481
  waveType: WAVE.SINE
590
482
  })
591
483
 
592
- setTimeout(function () {
593
- synth.playChord({
484
+ // Alternative voicing:
485
+ synth.playChord({
594
486
  notes: ['A3', 'E4', 'A3', 'E4', 'A3', 'E4'],
595
487
  detunes: [-9, -9, 0, 0, 9, 9],
596
488
  duration: 3200,
@@ -601,53 +493,34 @@ setTimeout(function () {
601
493
  filterType: 'lowpass',
602
494
  filterHz: 1500,
603
495
  panSpread: 0.8
604
- })
605
- }, 2400)
496
+ })
606
497
  ```
607
498
 
608
- Each repeated pitch is a voice. The second chord needs `maxVoices` of at least
609
- 6.
499
+ Each repeated pitch is a voice. The second chord needs `maxVoices` of at least 6.
610
500
 
611
- ## Chain patterns with `patternend`
501
+ ## Chain sequences with `sequenceend`
612
502
 
613
- ```javascript
614
- var jingles = [
615
- [{ note: 'C5' }, { note: 'E5' }, { note: 'G5' }],
616
- [{ note: 'G5' }, { note: 'E5' }, { note: 'C5' }]
617
- ]
618
- var nextJingle = 0
619
-
620
- function playNextJingle () {
621
- if (nextJingle >= jingles.length) return false
622
- return synth.playPattern({
623
- noteValue: '8n',
624
- attack: 5,
625
- release: 60,
626
- volume: 0.5,
627
- waveType: WAVE.SQUARE,
628
- steps: jingles[nextJingle++]
629
- })
630
- }
503
+ Use one sequence containing all steps for uninterrupted, sample-clock phrasing. The asynchronous completion event is useful for presentation or launching an unrelated cue:
631
504
 
632
- function onPatternEnd (event) {
633
- if (event.completed) playNextJingle()
505
+ ```javascript
506
+ var jingleId = synth.playSequence({
507
+ stepDuration: 0.5,
508
+ voice: { attack: 5, release: 60, volume: 0.5, waveType: WAVE.SQUARE },
509
+ steps: [{ notes: [72] }, { notes: [76] }, { notes: [79] }]
510
+ })
511
+ function onSequenceEnd (event) {
512
+ if (event.sequenceId === jingleId && event.reason === 'completed') Ti.API.info('Jingle scheduled to completion')
634
513
  }
635
-
636
- synth.addEventListener('patternend', onPatternEnd)
637
- playNextJingle()
638
-
639
- // When the audio owner closes:
640
- synth.removeEventListener('patternend', onPatternEnd)
514
+ synth.addEventListener('sequenceend', onSequenceEnd)
515
+ // When the owner closes:
516
+ synth.removeEventListener('sequenceend', onSequenceEnd)
641
517
  ```
642
518
 
643
- The event reports `completed: false` when another pattern replaces the running
644
- one, so a listener that only chains on `completed: true` never double-starts.
519
+ A positive identifier acknowledges publication; 0 rejects the complete operation. Sequence completion does not guarantee audible silence: release envelopes and effect tails can continue.
645
520
 
646
521
  ## Rules across keys
647
522
 
648
- Some combinations are rejected even when every value is in range. This call
649
- returns `false` with `sustain_without_decay`, because a sustain below 1 with no
650
- decay would jump in level and click:
523
+ Some combinations are rejected even when every value is in range. This call returns `false` with `sustain_without_decay`, because a sustain below 1 with no decay would jump in level and click:
651
524
 
652
525
  ```javascript
653
526
  // Rejected on purpose: sustain needs a decay.
@@ -660,7 +533,7 @@ synth.playTone({ note: 'A4', decay: 200, sustain: 0.5, duration: 800 })
660
533
  ## Speaker-safe startup
661
534
 
662
535
  ```javascript
663
- var accepted = synth.startEngine({
536
+ var accepted = synth.start({
664
537
  maxVoices: 12,
665
538
  mixingProfile: 'speakerSafe'
666
539
  })
@@ -668,9 +541,7 @@ var accepted = synth.startEngine({
668
541
  if (accepted) synth.setVolume({ volume: 0.85 })
669
542
  ```
670
543
 
671
- This profile lowers source gain and the limiter ceiling. It cannot repair a
672
- speaker that physically struggles with a very low pitch, so test the real
673
- device and raise the pitch when needed.
544
+ This profile lowers source gain and the limiter ceiling. It cannot repair a speaker that physically struggles with a very low pitch, so test the real device and raise the pitch when needed.
674
545
 
675
546
  ## Validation at an input boundary
676
547
 
@@ -686,9 +557,7 @@ function playUserFrequency (input) {
686
557
  }
687
558
  ```
688
559
 
689
- The native contract rejects bad values rather than clamping them. Validation in
690
- the app lets you give the user a specific message before the call reaches the
691
- module.
560
+ The native contract rejects invalid values without clamping them. Validation in the app lets you give the user a specific message before the call reaches the module.
692
561
 
693
562
  ## Stop and clean up
694
563
 
@@ -699,14 +568,13 @@ synth.stopAll()
699
568
  synth.shutdown()
700
569
  ```
701
570
 
702
- `stopAll()` fades current voices but leaves pending pattern scheduling alone.
703
- `shutdown()` cancels the scheduler and releases the native stream.
571
+ `stopAll()` stops all native sequences, fades current voices and invalidates effect histories. `shutdown()` also releases the native stream. To stop only one sequence owner, use `stopSequence({ sequenceId })`.
704
572
 
705
- The last committed interactive demo is in the upstream [`example/app.js`](https://github.com/macCesar/TiSynthEngine/blob/95901c98af5353b86d9dc7c60b29ab9186b038cd/example/app.js); that commit does not contain the pending statistics example. For additional retro effects, playable xylophone interaction, memory pads, layered polyphony and cancellable repetition, continue with [Curated sound and interaction recipes](recipes.md).
573
+ Follow the [interactive demo setup and test checklist](../assets/example/README.md) to run [`example/app.js`](../assets/example/app.js). It includes headphone positioning, session selection, pattern completion/replacement and all three statistics methods. For a service-level statistics recipe, see [read engine statistics](guide.md#read-engine-statistics).
706
574
 
707
575
  ## Positioned headphone effect
708
576
 
709
- Check the installed coordinate limits and keep the engine running. This JavaScript places one generated noise effect above and to the right; it does not change other sound calls:
577
+ With the engine running, check the installed coordinate limits. This example places one generated noise effect above and to the right. Other sound calls keep their existing placement:
710
578
 
711
579
  ```javascript
712
580
  if (!contract.limits.spatialX || !contract.limits.spatialY) {
@@ -725,3 +593,5 @@ if (!synth.playTone({
725
593
  ```
726
594
 
727
595
  For app switches, amplitude sliders, all nine screen positions and routing multiple effects through one service, use the [binaural integration guide](guide.md#integrate-binaural-in-a-2d-app). Omitting both coordinates disables binaural for that event; setting both to zero keeps it enabled at the front center.
596
+
597
+ Continue with [advanced examples](advanced-examples.md).