@maccesar/titools 5.1.0 → 5.2.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 +44 -12
- package/agents/ti-pro.md +4 -0
- package/commands/ti-audit.md +4 -0
- package/lib/config.js +2 -0
- package/package.json +1 -1
- package/skills/purgetss/SKILL.md +4 -4
- package/skills/purgetss/references/EXAMPLES.md +3 -3
- package/skills/purgetss/references/adopting-purgetss.md +2 -2
- package/skills/purgetss/references/animation-advanced.md +2 -2
- package/skills/purgetss/references/arbitrary-values.md +8 -5
- package/skills/purgetss/references/class-categories.md +5 -5
- package/skills/purgetss/references/class-index-properties.md +6 -6
- package/skills/purgetss/references/class-index.md +2 -2
- package/skills/purgetss/references/cli-commands.md +2 -2
- package/skills/purgetss/references/dynamic-component-creation.md +2 -2
- package/skills/purgetss/references/grid-layout.md +1 -1
- package/skills/purgetss/references/migration-guide.md +34 -4
- package/skills/purgetss/references/multi-density-images.md +6 -6
- package/skills/purgetss/references/platform-modifiers.md +21 -0
- package/skills/purgetss/references/ui-ux-design.md +1 -1
- package/skills/purgetss/references/version-history.md +9 -4
- package/skills/ti-admob-unified/SKILL.md +44 -0
- package/skills/ti-admob-unified/agents/openai.yaml +4 -0
- package/skills/ti-admob-unified/assets/example/app.js +1003 -0
- package/skills/ti-admob-unified/assets/example/lib/ads.js +618 -0
- package/skills/ti-admob-unified/assets/example/library-usage.js +104 -0
- package/skills/ti-admob-unified/references/api-reference.md +592 -0
- package/skills/ti-admob-unified/references/banners.md +435 -0
- package/skills/ti-admob-unified/references/contributing.md +228 -0
- package/skills/ti-admob-unified/references/dependencies.md +85 -0
- package/skills/ti-admob-unified/references/fullscreen.md +223 -0
- package/skills/ti-admob-unified/references/game-monetization.md +17 -0
- package/skills/ti-admob-unified/references/getting-started.md +275 -0
- package/skills/ti-admob-unified/references/index.md +30 -0
- package/skills/ti-admob-unified/references/integration.md +85 -0
- package/skills/ti-admob-unified/references/library.md +189 -0
- package/skills/ti-admob-unified/references/mediation.md +132 -0
- package/skills/ti-admob-unified/references/migration.md +255 -0
- package/skills/ti-admob-unified/references/module-changelog.md +54 -0
- package/skills/ti-admob-unified/references/module-license.txt +21 -0
- package/skills/ti-admob-unified/references/privacy.md +254 -0
- package/skills/ti-admob-unified/references/roadmap.md +146 -0
- package/skills/ti-admob-unified/references/source-notes.md +33 -0
- package/skills/ti-admob-unified/references/source-snapshot.json +440 -0
- package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
- package/skills/ti-game/SKILL.md +10 -1
- package/skills/ti-game/references/api.md +10 -2
- package/skills/ti-game/references/debugging-performance.md +6 -0
- package/skills/ti-game/references/examples.md +11 -1
- package/skills/ti-game/references/patterns.md +24 -2
- package/skills/ti-game/references/physics.md +502 -0
- package/skills/ti-game/references/project-setup.md +23 -1
- package/skills/ti-game/references/recipes-action.md +46 -14
- package/skills/ti-game/references/recipes-adventure-effects.md +12 -1
- package/skills/ti-game/references/roadmap.md +2 -2
- package/skills/ti-game/references/tilemaps.md +2 -1
- package/skills/ti-game-assets/SKILL.md +53 -0
- package/skills/ti-game-assets/agents/openai.yaml +4 -0
- package/skills/ti-game-assets/references/alpha.md +26 -0
- package/skills/ti-game-assets/references/art-direction.md +37 -0
- package/skills/ti-game-assets/references/integration.md +49 -0
- package/skills/ti-game-assets/references/manifest.example.json +429 -0
- package/skills/ti-game-assets/references/manifest.md +39 -0
- package/skills/ti-game-assets/references/offline-audio.md +33 -0
- package/skills/ti-game-assets/references/prompts.md +33 -0
- package/skills/ti-game-assets/references/repeating-textures.md +17 -0
- package/skills/ti-game-assets/references/responsible-ads.md +31 -0
- package/skills/ti-game-assets/references/sound-assets.md +42 -0
- package/skills/ti-game-assets/references/vector-sources.md +21 -0
- package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
- package/skills/ti-game-assets/scripts/requirements.txt +1 -0
- package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
- package/skills/ti-synthengine/SKILL.md +37 -13
- package/skills/ti-synthengine/references/api.md +397 -31
- package/skills/ti-synthengine/references/audio-engine.md +235 -14
- package/skills/ti-synthengine/references/examples.md +396 -17
- package/skills/ti-synthengine/references/guide.md +331 -12
- package/skills/ti-synthengine/references/recipes.md +212 -22
- package/skills/ti-synthengine/references/recommendations.md +53 -16
- package/skills/ti-synthengine/references/sound-design.md +359 -15
- package/skills/ti-synthengine/references/source-notes.md +121 -0
- package/skills/ti-synthengine/references/source-snapshot.json +105 -0
- package/skills/ti-synthengine/references/stats-performance.json +2992 -0
- package/skills/ti-synthengine/references/stats-performance.md +38 -0
- package/skills/ti-synthengine/references/troubleshooting.md +61 -4
|
@@ -1,13 +1,34 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine 1.0.0 documentation
|
|
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/guide.md` SHA-256: `c5fb4ff34ec425487221b80d6793bda24aa328281ab9ccb02e65b0065e54842e`.
|
|
2
2
|
|
|
3
3
|
# Guide
|
|
4
4
|
|
|
5
5
|
This guide covers the normal path from installing TiSynthEngine to releasing
|
|
6
6
|
the native audio resources when the owning screen closes.
|
|
7
7
|
|
|
8
|
+
<!-- TOC-START -->
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
- [Install the module](#install-the-module)
|
|
12
|
+
- [Load and verify the contract](#load-and-verify-the-contract)
|
|
13
|
+
- [Lifecycle](#lifecycle)
|
|
14
|
+
- [Play the first sound](#play-the-first-sound)
|
|
15
|
+
- [Choose a pitch](#choose-a-pitch)
|
|
16
|
+
- [Shape a tone](#shape-a-tone)
|
|
17
|
+
- [Settle, filter and modulate](#settle-filter-and-modulate)
|
|
18
|
+
- [Shape noise and waveforms](#shape-noise-and-waveforms)
|
|
19
|
+
- [Play simultaneous notes](#play-simultaneous-notes)
|
|
20
|
+
- [Play a pattern](#play-a-pattern)
|
|
21
|
+
- [Know when a pattern ends](#know-when-a-pattern-ends)
|
|
22
|
+
- [Master level and stopping](#master-level-and-stopping)
|
|
23
|
+
- [Output route changes](#output-route-changes)
|
|
24
|
+
- [Integrate binaural in a 2D app](#integrate-binaural-in-a-2d-app)
|
|
25
|
+
- [Next steps](#next-steps)
|
|
26
|
+
|
|
27
|
+
<!-- TOC-END -->
|
|
28
|
+
|
|
8
29
|
## Install the module
|
|
9
30
|
|
|
10
|
-
|
|
31
|
+
For the unreleased beta, use the packages built from the current module source in `android/dist` and `ios/dist`. The [latest release](https://github.com/macCesar/TiSynthEngine/releases/latest) is a separate package snapshot; it does not update when source files change. The archive names are:
|
|
11
32
|
|
|
12
33
|
- `ti.synthengine-android-1.0.0.zip`
|
|
13
34
|
- `ti.synthengine-iphone-1.0.0.zip`
|
|
@@ -22,8 +43,8 @@ Titanium CLI. Pin the same version for both platforms:
|
|
|
22
43
|
</modules>
|
|
23
44
|
```
|
|
24
45
|
|
|
25
|
-
TiSynthEngine requires Titanium SDK 13.
|
|
26
|
-
requires iOS
|
|
46
|
+
TiSynthEngine requires Titanium SDK 13.2.0.GA or newer. Android projects must
|
|
47
|
+
compile against API 35 or newer. The iOS module requires iOS 15 or newer.
|
|
27
48
|
|
|
28
49
|
## Load and verify the contract
|
|
29
50
|
|
|
@@ -161,6 +182,126 @@ synth.playTone({
|
|
|
161
182
|
channels. `lfoFreq` is the vibrato rate in hertz and `lfoDepth` is its depth in
|
|
162
183
|
hertz.
|
|
163
184
|
|
|
185
|
+
## Settle, filter and modulate
|
|
186
|
+
|
|
187
|
+
A flat envelope suits beeps. For plucks, keys and bells, add `decay` and
|
|
188
|
+
`sustain`: the level falls from full scale to `sustain` over `decay`
|
|
189
|
+
milliseconds and holds there until the release.
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
synth.playTone({
|
|
193
|
+
note: 'E4',
|
|
194
|
+
duration: 900,
|
|
195
|
+
attack: 2,
|
|
196
|
+
decay: 260,
|
|
197
|
+
sustain: 0.18,
|
|
198
|
+
release: 380,
|
|
199
|
+
waveType: contract.waveTypes.TRIANGLE
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`sustain` below 1 needs a nonzero `decay`, and `attack` plus `decay` must fit in
|
|
204
|
+
`duration`. The 50 ms default attack counts, so set a short `attack` when the
|
|
205
|
+
decay is long.
|
|
206
|
+
|
|
207
|
+
`filterType` adds a resonant filter: `lowpass`, `highpass` or `bandpass`.
|
|
208
|
+
`filterHz` is the cutoff, `filterQ` the resonance, and `filterHzEnd` sweeps the
|
|
209
|
+
cutoff over the note.
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
synth.playTone({
|
|
213
|
+
note: 'A2',
|
|
214
|
+
duration: 1000,
|
|
215
|
+
release: 200,
|
|
216
|
+
waveType: contract.waveTypes.SAWTOOTH,
|
|
217
|
+
filterType: 'bandpass',
|
|
218
|
+
filterHz: 350,
|
|
219
|
+
filterHzEnd: 2400,
|
|
220
|
+
filterQ: 5
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Any `filterHz`, `filterHzEnd` or `filterQ` without a `filterType` other than
|
|
225
|
+
`none` is rejected with `filter_type_required`.
|
|
226
|
+
|
|
227
|
+
`fmDepth` makes a `SINE` voice an FM voice. `fmRatio` is the modulator
|
|
228
|
+
frequency as a multiple of the carrier, and `fmDepthEnd` moves the modulation
|
|
229
|
+
index over the note. Omit `fmDepthEnd` for no sweep; 0 is a real target.
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
synth.playTone({
|
|
233
|
+
note: 'C5',
|
|
234
|
+
duration: 2600,
|
|
235
|
+
attack: 1,
|
|
236
|
+
decay: 2000,
|
|
237
|
+
sustain: 0.02,
|
|
238
|
+
fmRatio: 3.5,
|
|
239
|
+
fmDepth: 4,
|
|
240
|
+
fmDepthEnd: 0
|
|
241
|
+
})
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`detune` shifts a pitch in cents. In a chord, `detunes` gives one offset per
|
|
245
|
+
pitch, so repeating a note with small offsets thickens it:
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
synth.playChord({
|
|
249
|
+
notes: ['A3', 'A3', 'E4', 'E4'],
|
|
250
|
+
detunes: [-8, 8, -8, 8],
|
|
251
|
+
duration: 2000,
|
|
252
|
+
waveType: contract.waveTypes.SAWTOOTH
|
|
253
|
+
})
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Shape noise and waveforms
|
|
257
|
+
|
|
258
|
+
Use `noiseRate` to hold each pseudo-random noise value instead of generating a
|
|
259
|
+
new one every sample. A low rate sounds sparse and grainy; a high rate moves
|
|
260
|
+
toward white noise. `noiseRateEnd` sweeps the update rate.
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
synth.playTone({
|
|
264
|
+
frequency: 120,
|
|
265
|
+
duration: 700,
|
|
266
|
+
attack: 1,
|
|
267
|
+
release: 520,
|
|
268
|
+
waveType: contract.waveTypes.NOISE,
|
|
269
|
+
noiseRate: 11000,
|
|
270
|
+
noiseRateEnd: 650,
|
|
271
|
+
lowPassHz: 2600,
|
|
272
|
+
bitDepth: 8
|
|
273
|
+
})
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`lowPassHz` is a per-voice one-pole filter. `bitDepth` applies amplitude
|
|
277
|
+
quantization from 2 to 16 bits. Zero disables either control.
|
|
278
|
+
|
|
279
|
+
Square waves accept a duty cycle and a duty-cycle sweep:
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
synth.playTone({
|
|
283
|
+
note: 'A3',
|
|
284
|
+
duration: 650,
|
|
285
|
+
release: 180,
|
|
286
|
+
waveType: contract.waveTypes.SQUARE,
|
|
287
|
+
pulseWidth: 0.12,
|
|
288
|
+
pulseWidthEnd: 0.82
|
|
289
|
+
})
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
For a caller-defined repeating shape, select `WAVETABLE` and provide 2 to 64
|
|
293
|
+
samples from -1 to 1. The table is stepped rather than interpolated.
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
synth.playTone({
|
|
297
|
+
note: 'D4',
|
|
298
|
+
duration: 500,
|
|
299
|
+
waveType: contract.waveTypes.WAVETABLE,
|
|
300
|
+
waveTable: [-1, -0.8, -0.1, 0.9, 0.4, 0.1, -0.2, -0.6],
|
|
301
|
+
bitDepth: 8
|
|
302
|
+
})
|
|
303
|
+
```
|
|
304
|
+
|
|
164
305
|
## Play simultaneous notes
|
|
165
306
|
|
|
166
307
|
Use `playChord` when notes belong to one musical event:
|
|
@@ -180,8 +321,7 @@ The chord is placed on the audio queue atomically. Its voices also share a
|
|
|
180
321
|
constant-power event gain. Several separate `playTone` calls are independent,
|
|
181
322
|
can begin in different callbacks, and are hotter when summed.
|
|
182
323
|
|
|
183
|
-
Use `pans` when
|
|
184
|
-
centered, and an array longer than the note array is rejected.
|
|
324
|
+
Use `pans` when notes need deliberate positions. Supplied entries override `panSpread`; missing entries use the spread position, or remain centered when spread is 0. An array longer than the note array is rejected.
|
|
185
325
|
|
|
186
326
|
```javascript
|
|
187
327
|
synth.playChord({
|
|
@@ -213,13 +353,43 @@ synth.playPattern({
|
|
|
213
353
|
|
|
214
354
|
Step timing is chosen in this order: step `duration`, step `noteValue`, pattern
|
|
215
355
|
`noteValue`, pattern `stepDuration`, then the 100 ms default. A step also
|
|
216
|
-
inherits the pattern's envelope, volume, waveform, pan, sweep
|
|
217
|
-
|
|
356
|
+
inherits the pattern's envelope, decay, sustain, volume, waveform, pan, sweep,
|
|
357
|
+
vibrato, noise, filter, resonant filter, FM, detune, pulse-width, wavetable and
|
|
358
|
+
quantization options. Values set on the step override the inherited value.
|
|
218
359
|
|
|
219
360
|
Only one pattern scheduler is active. Starting another pattern cancels the
|
|
220
361
|
pending steps of the previous pattern. It does not stop voices that have
|
|
221
362
|
already started.
|
|
222
363
|
|
|
364
|
+
## Know when a pattern ends
|
|
365
|
+
|
|
366
|
+
Each accepted pattern produces one `patternend` event. `completed` is `true`
|
|
367
|
+
when the pattern reaches its natural end and `false` when a newer pattern
|
|
368
|
+
replaces it. Use it to chain jingles or to release state that follows a
|
|
369
|
+
pattern.
|
|
370
|
+
|
|
371
|
+
```javascript
|
|
372
|
+
function onPatternEnd (event) {
|
|
373
|
+
if (event.completed) playNextJingle()
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
synth.addEventListener('patternend', onPatternEnd)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The listener belongs to the module, not to the pattern, so it outlives the
|
|
380
|
+
screen unless you remove it. Add it to the same cleanup that calls
|
|
381
|
+
`shutdown()`:
|
|
382
|
+
|
|
383
|
+
```javascript
|
|
384
|
+
function closeAudio () {
|
|
385
|
+
synth.removeEventListener('patternend', onPatternEnd)
|
|
386
|
+
if (running) synth.shutdown()
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
`shutdown()` discards a pending pattern without an event, so a closing owner
|
|
391
|
+
never receives a callback after cleanup.
|
|
392
|
+
|
|
223
393
|
## Master level and stopping
|
|
224
394
|
|
|
225
395
|
```javascript
|
|
@@ -238,15 +408,164 @@ other output routes change. The note crossing the transition may end early;
|
|
|
238
408
|
new notes can be played normally after the system settles. Do not restart the
|
|
239
409
|
whole app for a route change.
|
|
240
410
|
|
|
241
|
-
On iOS, `startEngine()` activates a `Playback` audio session. That
|
|
242
|
-
silent switch and normally interrupts other audio.
|
|
243
|
-
|
|
411
|
+
On iOS, `startEngine()` activates a `Playback` audio session by default. That
|
|
412
|
+
ignores the silent switch and normally interrupts other audio. Pass
|
|
413
|
+
`audioSession: 'ambient'` to respect the silent switch and mix with other apps;
|
|
414
|
+
read [coexisting with other audio](recommendations.md#coexisting-with-other-audio)
|
|
244
415
|
and test the final app on a device.
|
|
245
416
|
|
|
417
|
+
## Integrate binaural in a 2D app
|
|
418
|
+
|
|
419
|
+
The JavaScript app chooses a position for each sound; the native module generates and filters that voice. Installing the module or enabling an app switch does not convert existing `pan` calls into binaural calls. Update every playback path that should use positioning, including explosions, impacts, shots, lasers and repeated motor/thrust events. A central audio service can do this once for all effects that supply their current source position. Calls bypassing that service keep their existing behavior.
|
|
420
|
+
|
|
421
|
+
Keep the synthesis recipe (pitch, waveform, duration, envelope, filters, FM and volume). Replace its placement fields when submitting the event. This is per-event processing inside the native ports using the shared KEMAR FIR, rather than a global platform spatial-audio switch.
|
|
422
|
+
|
|
423
|
+
### Detect the installed feature
|
|
424
|
+
|
|
425
|
+
During the unreleased 1.0.0 beta, the version string alone does not prove that an installed binary has binaural support. Check both coordinate limits before enabling the controls:
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
var synth = require('ti.synthengine')
|
|
429
|
+
var contract = synth.getDefaults()
|
|
430
|
+
var supportsBinaural = !!(contract.limits &&
|
|
431
|
+
contract.limits.spatialX && contract.limits.spatialY)
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
If the limits are absent, disable the binaural switch and keep stereo playback. Replace the installed module package and rebuild and reinstall the app to use a new native binary. Updating `tiapp.xml` or copying module files does not change an app already installed on a device. Follow the [lifecycle](#lifecycle) above to start the engine before playing.
|
|
435
|
+
|
|
436
|
+
### Direction and amplitude are different controls
|
|
437
|
+
|
|
438
|
+
`spatialX`/`spatialY` are signed directions from -1 to 1. A 0 to 100% amplitude slider scales the position derived from the object; it does not select a direction. At height 100%, an upper object remains above and a lower object remains below. At height 0%, both are at the front-center height, while binaural remains enabled. Direct direction sliders would instead need -100 to +100%, divided by 100.
|
|
439
|
+
|
|
440
|
+
| App control | Effect |
|
|
441
|
+
| --- | --- |
|
|
442
|
+
| Binaural switch | Send spatial coordinates when on; omit both when off. |
|
|
443
|
+
| Horizontal amplitude, 0 to 100% | Scale each source's left/right coordinate; also controls stereo `pan` when binaural is off. |
|
|
444
|
+
| Height amplitude, 0 to 100% | Scale each source's below/above coordinate; inactive in stereo mode. |
|
|
445
|
+
| Master volume | Use `setVolume`; independent of direction and amplitude. |
|
|
446
|
+
|
|
447
|
+
These are app preferences, not options for `startEngine` or additional native methods. Keep the app's chosen defaults and saved preferences; the module does not impose a 100% slider default. Persist them in the app if they should survive restart. A switch change applies to newly submitted events, without restarting the engine or repositioning voices already playing.
|
|
448
|
+
|
|
449
|
+
With both amplitude controls at 100%, screen positions map as follows:
|
|
450
|
+
|
|
451
|
+
| Position | `spatialX` | `spatialY` |
|
|
452
|
+
| --- | ---: | ---: |
|
|
453
|
+
| Top left | -1 | 1 |
|
|
454
|
+
| Top center | 0 | 1 |
|
|
455
|
+
| Top right | 1 | 1 |
|
|
456
|
+
| Middle left | -1 | 0 |
|
|
457
|
+
| Center | 0 | 0 |
|
|
458
|
+
| Middle right | 1 | 0 |
|
|
459
|
+
| Bottom left | -1 | -1 |
|
|
460
|
+
| Bottom center | 0 | -1 |
|
|
461
|
+
| Bottom right | 1 | -1 |
|
|
462
|
+
|
|
463
|
+
### Convert screen positions when playing a tone
|
|
464
|
+
|
|
465
|
+
This example preserves a recipe, removes conflicting placement fields, and computes new coordinates for each call. Coordinates use the same local arena space: X grows right, Y grows down, and width/height are the arena dimensions. Convert world or camera coordinates into that space first. The example percentages are app choices; retain your own defaults.
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
var audioSettings = {
|
|
469
|
+
binaural: supportsBinaural,
|
|
470
|
+
horizontalPercent: 50,
|
|
471
|
+
heightPercent: 100
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function clamp (value, min, max) {
|
|
475
|
+
return Math.max(min, Math.min(max, value))
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function playPositionedTone (recipe, x, y, width, height) {
|
|
479
|
+
var values = [x, y, width, height]
|
|
480
|
+
if (!values.every(function (value) {
|
|
481
|
+
return typeof value === 'number' && isFinite(value)
|
|
482
|
+
}) || width <= 0 || height <= 0) {
|
|
483
|
+
throw new Error('Sound position needs finite coordinates and positive dimensions')
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
var options = Object.assign({}, recipe)
|
|
487
|
+
delete options.pan
|
|
488
|
+
delete options.panEnd
|
|
489
|
+
delete options.pans
|
|
490
|
+
delete options.panSpread
|
|
491
|
+
delete options.spatialX
|
|
492
|
+
delete options.spatialY
|
|
493
|
+
|
|
494
|
+
var horizontal = clamp(audioSettings.horizontalPercent, 0, 100) / 100
|
|
495
|
+
var vertical = clamp(audioSettings.heightPercent, 0, 100) / 100
|
|
496
|
+
var nx = clamp(2 * x / width - 1, -1, 1)
|
|
497
|
+
var ny = clamp(1 - 2 * y / height, -1, 1)
|
|
498
|
+
|
|
499
|
+
if (audioSettings.binaural && supportsBinaural) {
|
|
500
|
+
options.spatialX = nx * horizontal
|
|
501
|
+
options.spatialY = ny * vertical
|
|
502
|
+
} else {
|
|
503
|
+
options.pan = nx * horizontal
|
|
504
|
+
options.panEnd = options.pan
|
|
505
|
+
}
|
|
506
|
+
return synth.playTone(options)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
var explosion = {
|
|
510
|
+
frequency: 90,
|
|
511
|
+
waveType: contract.waveTypes.NOISE,
|
|
512
|
+
duration: 500,
|
|
513
|
+
attack: 2,
|
|
514
|
+
release: 200,
|
|
515
|
+
volume: 0.3
|
|
516
|
+
}
|
|
517
|
+
if (!playPositionedTone(explosion, 900, 100, 1000, 800)) {
|
|
518
|
+
Ti.API.warn('Positioned explosion rejected')
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Connect a Titanium switch's `change` event to `audioSettings.binaural = event.value`; connect the two sliders' `change` events to `horizontalPercent` and `heightPercent`. Configure slider minimum 0 and maximum 100, and disable the height slider while the switch is off. Validate restored preference values as finite numbers within those ranges before using them. Do not pass these preference names to `playTone`: that would return `false` with `unsupported_option`.
|
|
523
|
+
|
|
524
|
+
Supply the current position whenever an effect fires. A projectile launch cue can use the launch position; an impact uses the collision position. Repeated short motor events can use successive positions. A single active tone cannot move after submission because this API has no per-voice handle or position-update method. Stereo `panEnd` movement cannot be combined with binaural; do not continually retrigger long sounds every frame to imitate movement.
|
|
525
|
+
|
|
526
|
+
### Chords and patterns
|
|
527
|
+
|
|
528
|
+
A chord shares one binaural position across all its voices. Remove `pans` and `panSpread` when sending coordinates. For different simultaneous source positions, use independent tone events and account for their different gain/overlap behavior:
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
if (!synth.playChord({
|
|
532
|
+
notes: ['C4', 'E4', 'G4'],
|
|
533
|
+
duration: 500,
|
|
534
|
+
volume: 0.3,
|
|
535
|
+
spatialX: -0.5,
|
|
536
|
+
spatialY: 0.7
|
|
537
|
+
})) Ti.API.warn('Spatial chord rejected')
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
Pattern steps inherit coordinates from the root and can override either one. Remove conflicting stereo fields from both the root and every step; even `pan: 0` conflicts with a spatial coordinate. A step cannot opt out of inherited binaural by passing zero coordinates; use a pattern without spatial coordinates on its root for a mix of spatial and stereo steps.
|
|
541
|
+
|
|
542
|
+
```javascript
|
|
543
|
+
if (!synth.playPattern({
|
|
544
|
+
stepDuration: 300,
|
|
545
|
+
attack: 5,
|
|
546
|
+
release: 80,
|
|
547
|
+
volume: 0.3,
|
|
548
|
+
spatialX: 0,
|
|
549
|
+
spatialY: 1,
|
|
550
|
+
steps: [
|
|
551
|
+
{ note: 'C4' },
|
|
552
|
+
{ note: 'E4', spatialY: -1 },
|
|
553
|
+
{ rest: true },
|
|
554
|
+
{ note: 'G4', spatialX: 1, spatialY: 0 }
|
|
555
|
+
]
|
|
556
|
+
})) Ti.API.warn('Spatial pattern rejected')
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Listen and measure
|
|
560
|
+
|
|
561
|
+
Use stereo headphones and compare isolated broadband noise above/below before testing many collisions. Perceived elevation varies with the listener, headphones and spectrum; low sine tones are weak tests. The output is still stereo, with a front plane and no rear sources, distance attenuation, room reflections or head tracking. A 100% slider uses the modeled directional range; it does not guarantee strong localization for every person.
|
|
562
|
+
|
|
563
|
+
CPU cost depends on concurrent spatial voices and their short tails, not the total number of effects over a game. Measure the intended device and realistic event overlap. Host replay is a comparison tool, not device CPU, battery, heat or underrun evidence. See [the renderer](audio-engine.md#binaural-front-plane-renderer) and [diagnostic checks](troubleshooting.md#binaural-does-not-change-the-position).
|
|
564
|
+
|
|
246
565
|
## Next steps
|
|
247
566
|
|
|
248
567
|
- [API reference](api.md) for every accepted key and limit.
|
|
249
|
-
- [Sound design](sound-design.md) for waveform and
|
|
568
|
+
- [Sound design](sound-design.md) for waveform, envelope, filter and FM recipes.
|
|
250
569
|
- [Examples](examples.md) for copy-ready sounds and patterns.
|
|
251
570
|
- [Troubleshooting](troubleshooting.md) when a call returns `false` or audio is
|
|
252
571
|
silent.
|