@maccesar/titools 5.1.1 → 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.
- package/CONTRIBUTING.md +69 -0
- package/README.md +112 -72
- package/agents/ti-pro.md +5 -1
- package/commands/ti-audit.md +5 -1
- package/docs/examples.md +91 -0
- package/docs/knowledge-index.md +57 -0
- package/lib/config.js +2 -0
- package/lib/downloader.js +1 -1
- package/package.json +4 -2
- package/skills/purgetss/references/multi-density-images.md +1 -1
- package/skills/ti-admob-unified/SKILL.md +58 -0
- package/skills/ti-admob-unified/agents/openai.yaml +4 -0
- package/skills/ti-admob-unified/assets/example/app.js +1207 -0
- package/skills/ti-admob-unified/assets/example/lib/ads.js +646 -0
- package/skills/ti-admob-unified/assets/example/library-usage.js +136 -0
- package/skills/ti-admob-unified/references/api-reference.md +615 -0
- package/skills/ti-admob-unified/references/banners.md +435 -0
- package/skills/ti-admob-unified/references/contributing.md +233 -0
- package/skills/ti-admob-unified/references/dependencies.md +91 -0
- package/skills/ti-admob-unified/references/fullscreen.md +238 -0
- package/skills/ti-admob-unified/references/game-monetization.md +148 -0
- package/skills/ti-admob-unified/references/getting-started.md +319 -0
- package/skills/ti-admob-unified/references/index.md +33 -0
- package/skills/ti-admob-unified/references/integration.md +87 -0
- package/skills/ti-admob-unified/references/library.md +220 -0
- package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
- package/skills/ti-admob-unified/references/mediation.md +145 -0
- package/skills/ti-admob-unified/references/migration.md +259 -0
- package/skills/ti-admob-unified/references/module-changelog.md +84 -0
- package/skills/ti-admob-unified/references/module-license.txt +21 -0
- package/skills/ti-admob-unified/references/privacy.md +281 -0
- package/skills/ti-admob-unified/references/production.md +67 -0
- package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
- package/skills/ti-admob-unified/references/roadmap.md +143 -0
- package/skills/ti-admob-unified/references/source-notes.md +80 -0
- package/skills/ti-admob-unified/references/source-snapshot.json +644 -0
- package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
- package/skills/ti-game/SKILL.md +31 -5
- package/skills/ti-game/references/api.md +36 -24
- package/skills/ti-game/references/debugging-performance.md +22 -7
- package/skills/ti-game/references/examples.md +113 -20
- package/skills/ti-game/references/input.md +101 -0
- package/skills/ti-game/references/patterns.md +56 -10
- package/skills/ti-game/references/physics.md +530 -0
- package/skills/ti-game/references/project-setup.md +131 -47
- package/skills/ti-game/references/recipes-action.md +149 -18
- package/skills/ti-game/references/recipes-adventure-effects.md +47 -4
- package/skills/ti-game/references/roadmap.md +11 -4
- package/skills/ti-game/references/source-notes.md +81 -0
- package/skills/ti-game/references/tilemaps.md +13 -4
- 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 +77 -67
- package/skills/ti-synthengine/assets/example/README.md +84 -0
- package/skills/ti-synthengine/assets/example/app.js +1178 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
- package/skills/ti-synthengine/references/advanced-examples.md +174 -0
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
- package/skills/ti-synthengine/references/api.md +573 -153
- package/skills/ti-synthengine/references/audio-engine.md +272 -124
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/examples.md +355 -106
- 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 +1918 -0
- package/skills/ti-synthengine/references/guide.md +426 -75
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +1146 -0
- package/skills/ti-synthengine/references/physical-validation.json +1457 -0
- package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
- package/skills/ti-synthengine/references/recipes.md +434 -420
- package/skills/ti-synthengine/references/recommendations.md +72 -85
- package/skills/ti-synthengine/references/sound-design.md +375 -155
- package/skills/ti-synthengine/references/source-notes.md +109 -0
- package/skills/ti-synthengine/references/source-snapshot.json +452 -0
- package/skills/ti-synthengine/references/stats-performance.json +2992 -0
- package/skills/ti-synthengine/references/stats-performance.md +82 -0
- package/skills/ti-synthengine/references/studio-validation.json +6202 -0
- package/skills/ti-synthengine/references/troubleshooting.md +123 -78
- package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
- package/skills/ti-synthengine/references/unified-validation.json +314 -0
- package/AGENTS-VERCEL-RESEARCH.md +0 -175
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine 1.0.0 documentation at commit `
|
|
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
|
-
|
|
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')
|
|
@@ -19,7 +22,12 @@ if (contract.apiVersion !== '1.0.0') {
|
|
|
19
22
|
## Contents
|
|
20
23
|
|
|
21
24
|
- [Start once after layout](#start-once-after-layout)
|
|
22
|
-
- [
|
|
25
|
+
- [Built-in waveforms](#built-in-waveforms)
|
|
26
|
+
- [Variable-rate filtered noise](#variable-rate-filtered-noise)
|
|
27
|
+
- [Pulse-width sweep](#pulse-width-sweep)
|
|
28
|
+
- [Stepped wavetable](#stepped-wavetable)
|
|
29
|
+
- [Fixed controls on a chord](#fixed-controls-on-a-chord)
|
|
30
|
+
- [Wave-shaping pattern overrides](#wave-shaping-pattern-overrides)
|
|
23
31
|
- [Pitch sweep and pan movement](#pitch-sweep-and-pan-movement)
|
|
24
32
|
- [Vibrato](#vibrato)
|
|
25
33
|
- [Chord with an even spread](#chord-with-an-even-spread)
|
|
@@ -30,9 +38,17 @@ if (contract.apiVersion !== '1.0.0') {
|
|
|
30
38
|
- [Noise percussion](#noise-percussion)
|
|
31
39
|
- [Xylophone sketch](#xylophone-sketch)
|
|
32
40
|
- [Simon-style pads](#simon-style-pads)
|
|
41
|
+
- [Pluck and electric piano](#pluck-and-electric-piano)
|
|
42
|
+
- [Bell](#bell)
|
|
43
|
+
- [Wah and filtered laser](#wah-and-filtered-laser)
|
|
44
|
+
- [Kick and hi-hat in one pattern](#kick-and-hi-hat-in-one-pattern)
|
|
45
|
+
- [Detuned chords](#detuned-chords)
|
|
46
|
+
- [Chain sequences with `sequenceend`](#chain-sequences-with-sequenceend)
|
|
47
|
+
- [Rules across keys](#rules-across-keys)
|
|
33
48
|
- [Speaker-safe startup](#speaker-safe-startup)
|
|
34
49
|
- [Validation at an input boundary](#validation-at-an-input-boundary)
|
|
35
50
|
- [Stop and clean up](#stop-and-clean-up)
|
|
51
|
+
- [Positioned headphone effect](#positioned-headphone-effect)
|
|
36
52
|
|
|
37
53
|
<!-- TOC-END -->
|
|
38
54
|
|
|
@@ -47,10 +63,12 @@ function onPostLayout () {
|
|
|
47
63
|
timer = setTimeout(function () {
|
|
48
64
|
timer = null
|
|
49
65
|
win.removeEventListener('postlayout', onPostLayout)
|
|
50
|
-
started = synth.
|
|
66
|
+
started = synth.start({
|
|
51
67
|
maxVoices: 16,
|
|
52
|
-
mixingProfile: 'conservative'
|
|
68
|
+
mixingProfile: 'conservative',
|
|
69
|
+
audioSession: 'ambient'
|
|
53
70
|
})
|
|
71
|
+
if (!started) Ti.API.warn('Audio startup failed; keep playback controls disabled')
|
|
54
72
|
}, 250)
|
|
55
73
|
}
|
|
56
74
|
|
|
@@ -65,26 +83,111 @@ win.addEventListener('close', cleanup)
|
|
|
65
83
|
win.open()
|
|
66
84
|
```
|
|
67
85
|
|
|
68
|
-
|
|
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
|
+
|
|
88
|
+
## Built-in waveforms
|
|
69
89
|
|
|
70
90
|
```javascript
|
|
71
91
|
var names = ['SINE', 'SQUARE', 'SAWTOOTH', 'TRIANGLE', 'NOISE']
|
|
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
|
+
})
|
|
98
|
+
```
|
|
72
99
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
Noise ignores pitch; each sounding step still supplies one pitch array.
|
|
101
|
+
|
|
102
|
+
## Variable-rate filtered noise
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
synth.playTone({
|
|
106
|
+
frequency: 100,
|
|
107
|
+
duration: 760,
|
|
108
|
+
attack: 1,
|
|
109
|
+
release: 580,
|
|
110
|
+
volume: 0.65,
|
|
111
|
+
waveType: WAVE.NOISE,
|
|
112
|
+
noiseRate: 12000,
|
|
113
|
+
noiseRateEnd: 600,
|
|
114
|
+
lowPassHz: 2400,
|
|
115
|
+
bitDepth: 8
|
|
84
116
|
})
|
|
85
117
|
```
|
|
86
118
|
|
|
87
|
-
|
|
119
|
+
`noiseRate` starts at 12 kHz and falls to 600 Hz. `lowPassHz` removes high frequency energy after the 8-bit source quantizer.
|
|
120
|
+
|
|
121
|
+
## Pulse-width sweep
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
synth.playTone({
|
|
125
|
+
note: 'A3',
|
|
126
|
+
duration: 700,
|
|
127
|
+
attack: 3,
|
|
128
|
+
release: 180,
|
|
129
|
+
volume: 0.55,
|
|
130
|
+
waveType: WAVE.SQUARE,
|
|
131
|
+
pulseWidth: 0.1,
|
|
132
|
+
pulseWidthEnd: 0.86
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The square duty cycle moves from 10 percent to 86 percent over the event.
|
|
137
|
+
|
|
138
|
+
## Stepped wavetable
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
synth.playTone({
|
|
142
|
+
note: 'D4',
|
|
143
|
+
duration: 650,
|
|
144
|
+
attack: 4,
|
|
145
|
+
release: 220,
|
|
146
|
+
volume: 0.58,
|
|
147
|
+
waveType: WAVE.WAVETABLE,
|
|
148
|
+
waveTable: [-1, -0.7, -0.1, 0.8, 1, 0.35, -0.25, -0.55],
|
|
149
|
+
bitDepth: 8
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The table repeats as a stepped cycle. `bitDepth: 8` quantizes each table value before filtering and the amplitude envelope.
|
|
154
|
+
|
|
155
|
+
## Fixed controls on a chord
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
synth.playChord({
|
|
159
|
+
notes: ['A3', 'E4', 'A4'],
|
|
160
|
+
duration: 900,
|
|
161
|
+
attack: 8,
|
|
162
|
+
release: 220,
|
|
163
|
+
volume: 0.5,
|
|
164
|
+
waveType: WAVE.SQUARE,
|
|
165
|
+
pulseWidth: 0.22,
|
|
166
|
+
lowPassHz: 3600,
|
|
167
|
+
bitDepth: 8,
|
|
168
|
+
pans: [-0.65, 0, 0.65]
|
|
169
|
+
})
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Chord voices share fixed shaping controls. Sweep fields such as `pulseWidthEnd` and `noiseRateEnd` are not accepted by `playChord()`.
|
|
173
|
+
|
|
174
|
+
## Wave-shaping pattern overrides
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
synth.playSequence({
|
|
178
|
+
timeUnit: 'ms',
|
|
179
|
+
stepDuration: 210,
|
|
180
|
+
voice: { attack: 2, release: 80, volume: 0.48, waveType: WAVE.NOISE, noiseRate: 12000, lowPassHz: 2600, bitDepth: 8 },
|
|
181
|
+
steps: [
|
|
182
|
+
{ frequencies: [120], duration: 180, overrides: { release: 150, noiseRateEnd: 800 } },
|
|
183
|
+
{ rest: true, duration: 100 },
|
|
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] } }
|
|
186
|
+
]
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
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`.
|
|
88
191
|
|
|
89
192
|
## Pitch sweep and pan movement
|
|
90
193
|
|
|
@@ -102,8 +205,7 @@ synth.playTone({
|
|
|
102
205
|
})
|
|
103
206
|
```
|
|
104
207
|
|
|
105
|
-
Set `frequencyEnd: 0` to keep the starting pitch. Set `panEnd` equal to `pan`
|
|
106
|
-
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.
|
|
107
209
|
|
|
108
210
|
## Vibrato
|
|
109
211
|
|
|
@@ -120,8 +222,7 @@ synth.playTone({
|
|
|
120
222
|
})
|
|
121
223
|
```
|
|
122
224
|
|
|
123
|
-
`lfoFreq` and `lfoDepth` are both in hertz. A depth of 6 Hz is gentle around
|
|
124
|
-
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.
|
|
125
226
|
|
|
126
227
|
## Chord with an even spread
|
|
127
228
|
|
|
@@ -151,75 +252,63 @@ synth.playChord({
|
|
|
151
252
|
})
|
|
152
253
|
```
|
|
153
254
|
|
|
154
|
-
Use `playChord` for a known simultaneous group. Launching four separate tones
|
|
155
|
-
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.
|
|
156
256
|
|
|
157
257
|
## Tempo, note values and swing
|
|
158
258
|
|
|
159
259
|
```javascript
|
|
160
|
-
synth.
|
|
161
|
-
|
|
260
|
+
synth.setTempo({ bpm: 132 })
|
|
261
|
+
synth.playSequence({
|
|
262
|
+
timeUnit: 'beats',
|
|
263
|
+
stepDuration: 0.5,
|
|
162
264
|
swing: 0.12,
|
|
163
|
-
|
|
164
|
-
attack: 4,
|
|
165
|
-
release: 55,
|
|
166
|
-
volume: 0.55,
|
|
167
|
-
waveType: WAVE.SQUARE,
|
|
265
|
+
voice: { attack: 4, release: 55, volume: 0.55, waveType: WAVE.SQUARE },
|
|
168
266
|
steps: [
|
|
169
|
-
{
|
|
170
|
-
{
|
|
267
|
+
{ notes: [60] },
|
|
268
|
+
{ notes: [64] },
|
|
171
269
|
{ rest: true },
|
|
172
|
-
{
|
|
173
|
-
{
|
|
270
|
+
{ notes: [67] },
|
|
271
|
+
{ notes: [72], duration: 1 }
|
|
174
272
|
]
|
|
175
273
|
})
|
|
176
274
|
```
|
|
177
275
|
|
|
178
|
-
|
|
179
|
-
that relationship. 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.
|
|
180
277
|
|
|
181
278
|
## Millisecond pattern
|
|
182
279
|
|
|
183
280
|
```javascript
|
|
184
|
-
synth.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
panEnd: 0.4,
|
|
188
|
-
waveType: WAVE.TRIANGLE,
|
|
281
|
+
synth.playSequence({
|
|
282
|
+
timeUnit: 'ms',
|
|
283
|
+
stepDuration: 100,
|
|
284
|
+
voice: { pan: -0.4, panEnd: 0.4, waveType: WAVE.TRIANGLE },
|
|
189
285
|
steps: [
|
|
190
|
-
{
|
|
191
|
-
{
|
|
192
|
-
{
|
|
193
|
-
{
|
|
286
|
+
{ notes: [72], duration: 90 },
|
|
287
|
+
{ rest: true, duration: 20 },
|
|
288
|
+
{ notes: [74], duration: 90 },
|
|
289
|
+
{ rest: true, duration: 20 },
|
|
290
|
+
{ notes: [76], duration: 90 },
|
|
291
|
+
{ rest: true, duration: 20 },
|
|
292
|
+
{ notes: [79], duration: 180 }
|
|
194
293
|
]
|
|
195
294
|
})
|
|
196
295
|
```
|
|
197
296
|
|
|
198
|
-
`
|
|
199
|
-
sound itself lasts. The pattern root does not accept `duration`.
|
|
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.
|
|
200
298
|
|
|
201
299
|
## Step overrides
|
|
202
300
|
|
|
203
301
|
```javascript
|
|
204
|
-
synth.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
release: 90,
|
|
209
|
-
volume: 0.5,
|
|
210
|
-
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 },
|
|
211
307
|
steps: [
|
|
212
|
-
{
|
|
213
|
-
{
|
|
214
|
-
note: 'E4',
|
|
215
|
-
waveType: WAVE.SAWTOOTH,
|
|
216
|
-
pan: -0.7,
|
|
217
|
-
panEnd: 0.7,
|
|
218
|
-
lfoFreq: 7,
|
|
219
|
-
lfoDepth: 10
|
|
220
|
-
},
|
|
308
|
+
{ notes: [60] },
|
|
309
|
+
{ notes: [64], overrides: { waveType: WAVE.SAWTOOTH, pan: -0.7, panEnd: 0.7, lfoFreq: 7, lfoDepth: 10 } },
|
|
221
310
|
{ rest: true },
|
|
222
|
-
{
|
|
311
|
+
{ notes: [67], duration: 1, overrides: { volume: 0.75 } }
|
|
223
312
|
]
|
|
224
313
|
})
|
|
225
314
|
```
|
|
@@ -227,49 +316,37 @@ synth.playPattern({
|
|
|
227
316
|
## Noise percussion
|
|
228
317
|
|
|
229
318
|
```javascript
|
|
230
|
-
synth.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
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 },
|
|
235
324
|
steps: [
|
|
236
|
-
{
|
|
325
|
+
{ frequencies: [200], overrides: { attack: 1, decay: 70, sustain: 0, release: 60 } },
|
|
237
326
|
{ rest: true },
|
|
238
|
-
{
|
|
239
|
-
{
|
|
327
|
+
{ frequencies: [200], overrides: { attack: 1, decay: 32, sustain: 0, release: 25 } },
|
|
328
|
+
{ frequencies: [200], overrides: { attack: 1, decay: 32, sustain: 0, release: 25 } },
|
|
240
329
|
{ rest: true },
|
|
241
|
-
{
|
|
330
|
+
{ frequencies: [200], overrides: { attack: 1, decay: 52, sustain: 0, release: 44 } }
|
|
242
331
|
]
|
|
243
332
|
})
|
|
244
333
|
```
|
|
245
334
|
|
|
246
|
-
Every sounding step needs `
|
|
247
|
-
write a primitive step such as `200`; primitive steps return
|
|
248
|
-
`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.
|
|
249
336
|
|
|
250
337
|
## Xylophone sketch
|
|
251
338
|
|
|
252
339
|
```javascript
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
waveType: WAVE.TRIANGLE,
|
|
261
|
-
pan: pan
|
|
262
|
-
})
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
playBar('C5', -0.7)
|
|
266
|
-
setTimeout(function () { playBar('E5', -0.2) }, 130)
|
|
267
|
-
setTimeout(function () { playBar('G5', 0.3) }, 260)
|
|
268
|
-
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
|
+
})
|
|
269
347
|
```
|
|
270
348
|
|
|
271
|
-
|
|
272
|
-
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.
|
|
273
350
|
|
|
274
351
|
## Simon-style pads
|
|
275
352
|
|
|
@@ -300,10 +377,163 @@ function playAllPads () {
|
|
|
300
377
|
}
|
|
301
378
|
```
|
|
302
379
|
|
|
380
|
+
## Pluck and electric piano
|
|
381
|
+
|
|
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.
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
function pluck (note) {
|
|
386
|
+
return synth.playTone({
|
|
387
|
+
note: note,
|
|
388
|
+
duration: 900,
|
|
389
|
+
attack: 2,
|
|
390
|
+
decay: 260,
|
|
391
|
+
sustain: 0.18,
|
|
392
|
+
release: 380,
|
|
393
|
+
volume: 0.6,
|
|
394
|
+
waveType: WAVE.TRIANGLE
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function electricPiano (note) {
|
|
399
|
+
return synth.playTone({
|
|
400
|
+
note: note,
|
|
401
|
+
duration: 1800,
|
|
402
|
+
attack: 3,
|
|
403
|
+
decay: 700,
|
|
404
|
+
sustain: 0.3,
|
|
405
|
+
release: 600,
|
|
406
|
+
volume: 0.55,
|
|
407
|
+
waveType: WAVE.SINE,
|
|
408
|
+
fmRatio: 1,
|
|
409
|
+
fmDepth: 2.2,
|
|
410
|
+
fmDepthEnd: 0.25
|
|
411
|
+
})
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Bell
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
synth.playTone({
|
|
419
|
+
note: 'C5',
|
|
420
|
+
duration: 2600,
|
|
421
|
+
attack: 1,
|
|
422
|
+
decay: 2000,
|
|
423
|
+
sustain: 0.02,
|
|
424
|
+
release: 500,
|
|
425
|
+
volume: 0.5,
|
|
426
|
+
waveType: WAVE.SINE,
|
|
427
|
+
fmRatio: 3.5,
|
|
428
|
+
fmDepth: 4,
|
|
429
|
+
fmDepthEnd: 0
|
|
430
|
+
})
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
A fractional `fmRatio` puts the overtones between harmonics, which reads as metal. `fmDepthEnd: 0` lets the brightness die away completely.
|
|
434
|
+
|
|
435
|
+
## Wah and filtered laser
|
|
436
|
+
|
|
437
|
+
```javascript
|
|
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 } }]
|
|
446
|
+
})
|
|
447
|
+
```
|
|
448
|
+
|
|
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.
|
|
450
|
+
|
|
451
|
+
## Kick and hi-hat in one pattern
|
|
452
|
+
|
|
453
|
+
Each step can carry its own envelope and filter.
|
|
454
|
+
|
|
455
|
+
```javascript
|
|
456
|
+
synth.setTempo({ bpm: 110 })
|
|
457
|
+
synth.playSequence({
|
|
458
|
+
timeUnit: 'beats',
|
|
459
|
+
stepDuration: 0.5,
|
|
460
|
+
steps: [
|
|
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 } },
|
|
463
|
+
{ rest: true },
|
|
464
|
+
{ frequencies: [440], overrides: { waveType: WAVE.NOISE, attack: 1, decay: 30, sustain: 0.25, release: 45, volume: 0.45, filterType: 'highpass', filterHz: 7000 } }
|
|
465
|
+
]
|
|
466
|
+
})
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Detuned chords
|
|
470
|
+
|
|
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.
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
synth.playChord({
|
|
475
|
+
frequencies: [440, 440],
|
|
476
|
+
detunes: [-6, 6],
|
|
477
|
+
duration: 2000,
|
|
478
|
+
attack: 200,
|
|
479
|
+
release: 600,
|
|
480
|
+
volume: 0.55,
|
|
481
|
+
waveType: WAVE.SINE
|
|
482
|
+
})
|
|
483
|
+
|
|
484
|
+
// Alternative voicing:
|
|
485
|
+
synth.playChord({
|
|
486
|
+
notes: ['A3', 'E4', 'A3', 'E4', 'A3', 'E4'],
|
|
487
|
+
detunes: [-9, -9, 0, 0, 9, 9],
|
|
488
|
+
duration: 3200,
|
|
489
|
+
attack: 700,
|
|
490
|
+
release: 1300,
|
|
491
|
+
volume: 0.5,
|
|
492
|
+
waveType: WAVE.SAWTOOTH,
|
|
493
|
+
filterType: 'lowpass',
|
|
494
|
+
filterHz: 1500,
|
|
495
|
+
panSpread: 0.8
|
|
496
|
+
})
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Each repeated pitch is a voice. The second chord needs `maxVoices` of at least 6.
|
|
500
|
+
|
|
501
|
+
## Chain sequences with `sequenceend`
|
|
502
|
+
|
|
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:
|
|
504
|
+
|
|
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')
|
|
513
|
+
}
|
|
514
|
+
synth.addEventListener('sequenceend', onSequenceEnd)
|
|
515
|
+
// When the owner closes:
|
|
516
|
+
synth.removeEventListener('sequenceend', onSequenceEnd)
|
|
517
|
+
```
|
|
518
|
+
|
|
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.
|
|
520
|
+
|
|
521
|
+
## Rules across keys
|
|
522
|
+
|
|
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:
|
|
524
|
+
|
|
525
|
+
```javascript
|
|
526
|
+
// Rejected on purpose: sustain needs a decay.
|
|
527
|
+
var accepted = synth.playTone({ note: 'A4', sustain: 0.5 })
|
|
528
|
+
|
|
529
|
+
// Accepted: the same note with a decay.
|
|
530
|
+
synth.playTone({ note: 'A4', decay: 200, sustain: 0.5, duration: 800 })
|
|
531
|
+
```
|
|
532
|
+
|
|
303
533
|
## Speaker-safe startup
|
|
304
534
|
|
|
305
535
|
```javascript
|
|
306
|
-
var accepted = synth.
|
|
536
|
+
var accepted = synth.start({
|
|
307
537
|
maxVoices: 12,
|
|
308
538
|
mixingProfile: 'speakerSafe'
|
|
309
539
|
})
|
|
@@ -311,9 +541,7 @@ var accepted = synth.startEngine({
|
|
|
311
541
|
if (accepted) synth.setVolume({ volume: 0.85 })
|
|
312
542
|
```
|
|
313
543
|
|
|
314
|
-
This profile lowers source gain and the limiter ceiling. It cannot repair a
|
|
315
|
-
speaker that physically struggles with a very low pitch, so test the real
|
|
316
|
-
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.
|
|
317
545
|
|
|
318
546
|
## Validation at an input boundary
|
|
319
547
|
|
|
@@ -329,9 +557,7 @@ function playUserFrequency (input) {
|
|
|
329
557
|
}
|
|
330
558
|
```
|
|
331
559
|
|
|
332
|
-
The native contract rejects
|
|
333
|
-
the app lets you give the user a specific message before the call reaches the
|
|
334
|
-
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.
|
|
335
561
|
|
|
336
562
|
## Stop and clean up
|
|
337
563
|
|
|
@@ -342,7 +568,30 @@ synth.stopAll()
|
|
|
342
568
|
synth.shutdown()
|
|
343
569
|
```
|
|
344
570
|
|
|
345
|
-
`stopAll()` fades current voices
|
|
346
|
-
|
|
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 })`.
|
|
572
|
+
|
|
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).
|
|
574
|
+
|
|
575
|
+
## Positioned headphone effect
|
|
576
|
+
|
|
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:
|
|
578
|
+
|
|
579
|
+
```javascript
|
|
580
|
+
if (!contract.limits.spatialX || !contract.limits.spatialY) {
|
|
581
|
+
throw new Error('This installed module does not report binaural support')
|
|
582
|
+
}
|
|
583
|
+
if (!synth.playTone({
|
|
584
|
+
frequency: 90,
|
|
585
|
+
waveType: WAVE.NOISE,
|
|
586
|
+
duration: 500,
|
|
587
|
+
attack: 2,
|
|
588
|
+
release: 200,
|
|
589
|
+
volume: 0.3,
|
|
590
|
+
spatialX: 0.8,
|
|
591
|
+
spatialY: 0.75
|
|
592
|
+
})) Ti.API.warn('Positioned effect rejected')
|
|
593
|
+
```
|
|
594
|
+
|
|
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.
|
|
347
596
|
|
|
348
|
-
|
|
597
|
+
Continue with [advanced examples](advanced-examples.md).
|
|
Binary file
|
|
Binary file
|