@maccesar/titools 5.1.1 → 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/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,4 +1,4 @@
|
|
|
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/api.md` SHA-256: `60f9eda77f327bad6b66b0b6a49089c383a49a211c0691d09a0d715731434b56`.
|
|
2
2
|
|
|
3
3
|
# API reference
|
|
4
4
|
|
|
@@ -6,9 +6,32 @@ Module id: `ti.synthengine`
|
|
|
6
6
|
|
|
7
7
|
Public API version: `1.0.0`
|
|
8
8
|
|
|
9
|
-
Android and iOS expose the same
|
|
10
|
-
values and validation behavior. Option objects are strict: an unknown
|
|
11
|
-
rejected with `unsupported_option`.
|
|
9
|
+
Android and iOS expose the same eleven methods, one event, defaults, limits,
|
|
10
|
+
waveform values and validation behavior. Option objects are strict: an unknown
|
|
11
|
+
key is rejected with `unsupported_option`.
|
|
12
|
+
|
|
13
|
+
<!-- TOC-START -->
|
|
14
|
+
## Contents
|
|
15
|
+
|
|
16
|
+
- [Return values](#return-values)
|
|
17
|
+
- [`getDefaults()`](#getdefaults)
|
|
18
|
+
- [Limits](#limits)
|
|
19
|
+
- [`startEngine(options)`](#startengineoptions)
|
|
20
|
+
- [`playTone(options)`](#playtoneoptions)
|
|
21
|
+
- [`playChord(options)`](#playchordoptions)
|
|
22
|
+
- [`playPattern(options)`](#playpatternoptions)
|
|
23
|
+
- [Expressive controls](#expressive-controls)
|
|
24
|
+
- [`setVolume(options)`](#setvolumeoptions)
|
|
25
|
+
- [`stopAll()`](#stopall)
|
|
26
|
+
- [`shutdown()`](#shutdown)
|
|
27
|
+
- [`patternend` event](#patternend-event)
|
|
28
|
+
- [Strict validation](#strict-validation)
|
|
29
|
+
- [Binaural front-plane positioning](#binaural-front-plane-positioning)
|
|
30
|
+
- [`getStats()`](#getstats)
|
|
31
|
+
- [`setStatsEnabled(enabled)`](#setstatsenabledenabled)
|
|
32
|
+
- [`resetStats()`](#resetstats)
|
|
33
|
+
|
|
34
|
+
<!-- TOC-END -->
|
|
12
35
|
|
|
13
36
|
## Return values
|
|
14
37
|
|
|
@@ -16,7 +39,8 @@ rejected with `unsupported_option`.
|
|
|
16
39
|
Boolean. `true` means the request was accepted. `false` means validation
|
|
17
40
|
failed, the engine was unavailable, or the native command queue was full.
|
|
18
41
|
|
|
19
|
-
`
|
|
42
|
+
`setStatsEnabled` also returns Boolean acceptance.
|
|
43
|
+
`stopAll`, `resetStats` and `shutdown` do not return a value. `getDefaults` returns the
|
|
20
44
|
contract object described below.
|
|
21
45
|
|
|
22
46
|
## `getDefaults()`
|
|
@@ -35,6 +59,7 @@ The result has this shape:
|
|
|
35
59
|
defaults: {
|
|
36
60
|
maxVoices: 8,
|
|
37
61
|
mixingProfile: 'conservative',
|
|
62
|
+
audioSession: 'playback',
|
|
38
63
|
frequency: 440,
|
|
39
64
|
duration: 500,
|
|
40
65
|
attack: 50,
|
|
@@ -46,51 +71,89 @@ The result has this shape:
|
|
|
46
71
|
frequencyEnd: 0,
|
|
47
72
|
lfoFreq: 0,
|
|
48
73
|
lfoDepth: 0,
|
|
74
|
+
noiseRate: 0,
|
|
75
|
+
noiseRateEnd: 0,
|
|
76
|
+
lowPassHz: 0,
|
|
77
|
+
pulseWidth: 0.5,
|
|
78
|
+
pulseWidthEnd: 0,
|
|
79
|
+
waveTable: [],
|
|
80
|
+
bitDepth: 0,
|
|
49
81
|
panSpread: 0,
|
|
50
82
|
bpm: 120,
|
|
51
83
|
swing: 0,
|
|
52
|
-
stepDuration: 100
|
|
84
|
+
stepDuration: 100,
|
|
85
|
+
decay: 0,
|
|
86
|
+
sustain: 1,
|
|
87
|
+
filterType: 'none',
|
|
88
|
+
filterHz: 1000,
|
|
89
|
+
filterHzEnd: 0,
|
|
90
|
+
filterQ: 0.7071,
|
|
91
|
+
fmRatio: 1,
|
|
92
|
+
fmDepth: 0,
|
|
93
|
+
detune: 0
|
|
53
94
|
},
|
|
54
95
|
limits: { /* ranges listed below */ },
|
|
55
96
|
mixingProfiles: ['conservative', 'raw', 'balanced', 'speakerSafe'],
|
|
97
|
+
audioSessions: ['playback', 'ambient'],
|
|
98
|
+
filterTypes: ['none', 'lowpass', 'highpass', 'bandpass'],
|
|
56
99
|
waveTypes: {
|
|
57
100
|
SINE: 0,
|
|
58
101
|
SQUARE: 1,
|
|
59
102
|
SAWTOOTH: 2,
|
|
60
103
|
TRIANGLE: 3,
|
|
61
|
-
NOISE: 4
|
|
104
|
+
NOISE: 4,
|
|
105
|
+
WAVETABLE: 5
|
|
62
106
|
}
|
|
63
107
|
}
|
|
64
108
|
```
|
|
65
109
|
|
|
66
|
-
The maximum frequency is `sampleRate * 0.45`. Before the stream opens, the
|
|
110
|
+
`fmDepthEnd`, `detunes`, `spatialX` and `spatialY` have no reported default because omitting them is meaningful. Supplying a spatial coordinate enables binaural; the other coordinate then defaults to 0 internally. The maximum frequency is `sampleRate * 0.45`. Before the stream opens, the
|
|
67
111
|
reported fallback is 19,845 Hz. Read the object again after `startEngine()` if
|
|
68
112
|
the precise device limit matters.
|
|
69
113
|
|
|
70
114
|
## Limits
|
|
71
115
|
|
|
72
|
-
All ranges include their endpoints.
|
|
116
|
+
All ranges include their endpoints. `getDefaults().limits` uses `{ min, max }` objects. Sweep targets and array elements share the corresponding base range: `panEnd`/`pans[]` use `pan`, `frequencyEnd` accepts 0 through the frequency maximum, but only targets of at least 1 Hz enable a sweep (values below 1 disable it), `noiseRateEnd` uses `noiseRate`, `filterHzEnd` uses `filterHz` plus disabled value 0, `fmDepthEnd` uses `fmDepth`, and `detunes[]` uses `detune`. The sample-count key is `waveTableSamples`; there is no `limits.waveTable` object.
|
|
73
117
|
|
|
74
118
|
| Field | Minimum | Maximum | Unit or rule |
|
|
75
119
|
| --- | ---: | ---: | --- |
|
|
76
120
|
| `maxVoices` | 1 | 32 | integer voices |
|
|
77
121
|
| `frequency` | 1 | `sampleRate * 0.45` | Hz |
|
|
78
|
-
| `duration` | 1 | 60,000 | integer ms |
|
|
122
|
+
| `duration` | 1 | 60,000 | integer ms, including explicit pattern step duration |
|
|
79
123
|
| `attack` | 0 | 2,000 | integer ms; must be less than duration |
|
|
80
124
|
| `release` | 0 | 4,000 | integer ms; must fit after attack |
|
|
81
125
|
| `volume` | 0 | 2 | event or master multiplier |
|
|
82
|
-
| `waveType` | 0 |
|
|
126
|
+
| `waveType` | 0 | 5 | integer waveform value |
|
|
83
127
|
| `patternSteps` | 1 | 512 | object steps |
|
|
84
128
|
| `pan`, `panEnd`, `pans[]` | -1 | 1 | left to right |
|
|
129
|
+
| `spatialX`, `spatialY` | -1 | 1 | fixed binaural direction; omitted unless enabled |
|
|
85
130
|
| `panSpread` | 0 | 1 | chord stereo width |
|
|
86
|
-
| `frequencyEnd` | 0 | `sampleRate * 0.45` | Hz;
|
|
131
|
+
| `frequencyEnd` | 0 | `sampleRate * 0.45` | Hz; values below 1 disable the sweep |
|
|
87
132
|
| `lfoFreq` | 0 | 20,000 | Hz; 0 disables vibrato |
|
|
88
133
|
| `lfoDepth` | 0 | 20,000 | Hz |
|
|
134
|
+
| `noiseRate`, `noiseRateEnd` | 0 | `sampleRate * 0.45` | Hz; 0 selects white noise or disables the sweep |
|
|
135
|
+
| `lowPassHz` | 0 | `sampleRate * 0.45` | Hz; 0 disables the filter |
|
|
136
|
+
| `pulseWidth` | 0.05 | 0.95 | fraction of a square-wave cycle |
|
|
137
|
+
| `pulseWidthEnd` | 0 or 0.05 | 0.95 | 0 disables the sweep |
|
|
138
|
+
| `waveTable` | 2 samples | 64 samples | each value must be from -1 to 1; count range is `limits.waveTableSamples` |
|
|
139
|
+
| `bitDepth` | 0 or 2 | 16 | integer bits; 0 disables quantization |
|
|
89
140
|
| `bpm` | 20 | 300 | beats per minute |
|
|
90
141
|
| `swing` | -0.49 | 0.49 | alternating timing offset |
|
|
91
142
|
| `stepDuration` | 1 | 60,000 | ms |
|
|
92
|
-
|
|
93
|
-
|
|
143
|
+
| `decay` | 0 | 4,000 | integer ms; `attack` plus `decay` must fit in the event |
|
|
144
|
+
| `sustain` | 0 | 1 | level held after the decay |
|
|
145
|
+
| `filterHz` | 20 | `sampleRate * 0.45` | Hz |
|
|
146
|
+
| `filterHzEnd` | 0 or 20 | `sampleRate * 0.45` | Hz; 0 disables the sweep |
|
|
147
|
+
| `filterQ` | 0.5 | 10 | resonance |
|
|
148
|
+
| `fmRatio` | 0.1 | 16 | modulator frequency divided by carrier frequency |
|
|
149
|
+
| `fmDepth`, `fmDepthEnd` | 0 | 10 | modulation index in radians |
|
|
150
|
+
| `detune`, `detunes[]` | -1200 | 1200 | cents |
|
|
151
|
+
|
|
152
|
+
The allowed `noteValue` strings are `1n`, `2n`, `4n`, `8n` and `16n`, the
|
|
153
|
+
dotted values `2n.`, `4n.` and `8n.`, and the triplets `8t` and `16t`. A dotted
|
|
154
|
+
value lasts one and a half times as long as the plain one. Three triplet steps
|
|
155
|
+
fit in the time of two plain steps of the same name. Any other length is set
|
|
156
|
+
in milliseconds with `duration` or `stepDuration`.
|
|
94
157
|
|
|
95
158
|
## `startEngine(options)`
|
|
96
159
|
|
|
@@ -101,13 +164,20 @@ Starts the native stream. Calling it again while the engine is running returns
|
|
|
101
164
|
| --- | --- | --- | --- |
|
|
102
165
|
| `maxVoices` | integer | `8` | Size of the voice pool, from 1 to 32. |
|
|
103
166
|
| `mixingProfile` | string | `conservative` | `conservative`, `balanced`, `speakerSafe` or `raw`. Matching is case-insensitive. |
|
|
167
|
+
| `audioSession` | string | `playback` | iOS session category: `playback` or `ambient`. Matching is case-insensitive. Android validates the value and does not change its output. |
|
|
168
|
+
|
|
169
|
+
Options are validated even when the engine is already running; a second call with invalid options still returns `false`.
|
|
104
170
|
|
|
105
|
-
The profile
|
|
106
|
-
before starting again with different values.
|
|
171
|
+
The profile, voice capacity and audio session stay fixed for the session. Call
|
|
172
|
+
`shutdown()` before starting again with different values.
|
|
173
|
+
|
|
174
|
+
On iOS, `playback` ignores the silent switch and interrupts other apps' audio.
|
|
175
|
+
`ambient` is muted by the silent switch and mixes with other apps' audio. The
|
|
176
|
+
module applies the category again when it resumes after a route change.
|
|
107
177
|
|
|
108
178
|
## `playTone(options)`
|
|
109
179
|
|
|
110
|
-
Plays one pitch. It uses 440 Hz when neither pitch field is present.
|
|
180
|
+
Plays one pitch. It uses 440 Hz when neither pitch field is present. Note names accept C through B or DO, RE, MI, FA, SOL, LA, SI and TI, followed by optional `#` or lowercase `b` and one octave digit from 0 to 9. Letters are case-insensitive, the omitted octave is 4, and A4 is 440 Hz. A valid named pitch is limited to the active frequency ceiling when rendered; an out-of-range numeric `frequency` is rejected.
|
|
111
181
|
|
|
112
182
|
| Key | Type | Default | Description |
|
|
113
183
|
| --- | --- | ---: | --- |
|
|
@@ -117,12 +187,37 @@ Plays one pitch. It uses 440 Hz when neither pitch field is present.
|
|
|
117
187
|
| `attack` | integer | `50` | Fade-in time in ms. |
|
|
118
188
|
| `release` | integer | `50` | Fade-out time in ms. |
|
|
119
189
|
| `volume` | number | `1` | Event level multiplier. |
|
|
120
|
-
| `waveType` | integer | `SINE` | One of the
|
|
190
|
+
| `waveType` | integer | `SINE` | One of the six waveform values. |
|
|
121
191
|
| `pan` | number | `0` | Starting stereo position. |
|
|
122
|
-
| `panEnd` | number | `
|
|
123
|
-
| `frequencyEnd` | number | `0` | Ending frequency;
|
|
192
|
+
| `panEnd` | number | same as `pan` | Ending stereo position; omission keeps the position fixed. |
|
|
193
|
+
| `frequencyEnd` | number | `0` | Ending frequency; values below 1 keep the starting pitch. |
|
|
124
194
|
| `lfoFreq` | number | `0` | Vibrato rate in Hz. |
|
|
125
195
|
| `lfoDepth` | number | `0` | Vibrato depth in Hz. |
|
|
196
|
+
| `noiseRate` | number | `0` | Noise update rate; 0 produces a fresh value per sample. |
|
|
197
|
+
| `noiseRateEnd` | number | `0` | Ending noise update rate; 0 disables the sweep. |
|
|
198
|
+
| `lowPassHz` | number | `0` | Per-voice low-pass cutoff; 0 bypasses it. |
|
|
199
|
+
| `pulseWidth` | number | `0.5` | Duty cycle for `SQUARE`. |
|
|
200
|
+
| `pulseWidthEnd` | number | `0` | Ending duty cycle; 0 disables the sweep. |
|
|
201
|
+
| `waveTable` | number[] | none | Required for `WAVETABLE`; 2 to 64 normalized samples. |
|
|
202
|
+
| `bitDepth` | integer | `0` | Amplitude quantization from 2 to 16 bits; 0 disables it. |
|
|
203
|
+
| `decay` | integer | `0` | Milliseconds to fall from full level to `sustain` after the attack. |
|
|
204
|
+
| `sustain` | number | `1` | Level held after the decay, from 0 to 1. |
|
|
205
|
+
| `filterType` | string | `none` | `none`, `lowpass`, `highpass` or `bandpass`. Case-insensitive. |
|
|
206
|
+
| `filterHz` | number | `1000` | Cutoff, or center for `bandpass`, in Hz. |
|
|
207
|
+
| `filterHzEnd` | number | `0` | Ending cutoff; 0 disables the sweep. |
|
|
208
|
+
| `filterQ` | number | `0.7071` | Resonance, from 0.5 to 10. |
|
|
209
|
+
| `fmRatio` | number | `1` | Modulator frequency as a multiple of the carrier. |
|
|
210
|
+
| `fmDepth` | number | `0` | Modulation index; 0 leaves FM off unless `fmDepthEnd` is set. |
|
|
211
|
+
| `fmDepthEnd` | number | none | Ending index. Omit it for no sweep; 0 is a valid target. |
|
|
212
|
+
| `detune` | number | `0` | Pitch offset in cents, applied to `frequency` and `frequencyEnd`. |
|
|
213
|
+
| `spatialX` | number | omitted | Fixed left/right binaural direction; see [positioning](#binaural-front-plane-positioning). |
|
|
214
|
+
| `spatialY` | number | omitted | Fixed below/above binaural direction. |
|
|
215
|
+
|
|
216
|
+
Omitted attack/release values are shortened to fit an event smaller than the default envelope. Explicit values must satisfy the documented envelope rules. `getDefaults()` reports the baseline defaults, not these per-event adjustments.
|
|
217
|
+
|
|
218
|
+
The controls from `decay` to `detune` are described in
|
|
219
|
+
[Expressive controls](#expressive-controls), including the rules that tie them
|
|
220
|
+
together.
|
|
126
221
|
|
|
127
222
|
## `playChord(options)`
|
|
128
223
|
|
|
@@ -140,13 +235,29 @@ required.
|
|
|
140
235
|
| `waveType` | integer | `SINE` | Waveform for every voice. |
|
|
141
236
|
| `panSpread` | number | `0` | Even stereo spread from 0 to 1. |
|
|
142
237
|
| `pans` | number[] | none | Per-note pan positions; overrides spread for supplied entries. |
|
|
143
|
-
|
|
144
|
-
`
|
|
145
|
-
|
|
238
|
+
| `noiseRate` | number | `0` | Fixed update rate for noise voices. |
|
|
239
|
+
| `lowPassHz` | number | `0` | Fixed low-pass cutoff for every voice. |
|
|
240
|
+
| `pulseWidth` | number | `0.5` | Fixed square-wave duty cycle. |
|
|
241
|
+
| `waveTable` | number[] | none | Required when `waveType` is `WAVETABLE`. |
|
|
242
|
+
| `bitDepth` | integer | `0` | Fixed amplitude quantization; 0 disables it. |
|
|
243
|
+
| `decay` | integer | `0` | Fixed decay time shared by every voice. |
|
|
244
|
+
| `sustain` | number | `1` | Fixed sustain level shared by every voice. |
|
|
245
|
+
| `filterType` | string | `none` | Fixed filter type for every voice. |
|
|
246
|
+
| `filterHz` | number | `1000` | Fixed cutoff or center in Hz. |
|
|
247
|
+
| `filterQ` | number | `0.7071` | Fixed resonance. |
|
|
248
|
+
| `fmRatio` | number | `1` | Fixed modulator ratio. |
|
|
249
|
+
| `fmDepth` | number | `0` | Fixed modulation index; sine voices only. |
|
|
250
|
+
| `detunes` | number[] | none | Per-note offsets in cents, matched to the pitch array. |
|
|
251
|
+
| `spatialX` | number | omitted | Shared fixed left/right binaural direction. |
|
|
252
|
+
| `spatialY` | number | omitted | Shared fixed below/above binaural direction. |
|
|
253
|
+
|
|
254
|
+
`pans` and `detunes` cannot contain more entries than the pitch array, and
|
|
255
|
+
missing detune entries count as 0. Missing `pans` entries use the position computed from `panSpread`, or 0 when no spread is set. `playChord` does not accept sweep, vibrato or
|
|
256
|
+
`detune` fields.
|
|
146
257
|
|
|
147
258
|
## `playPattern(options)`
|
|
148
259
|
|
|
149
|
-
Schedules a
|
|
260
|
+
Schedules one pitch or rest per step. Swing can cause successive voices to overlap; a step does not contain a chord. The required `steps` array must contain only
|
|
150
261
|
objects. A string or number step is rejected with `invalid_step_type`.
|
|
151
262
|
|
|
152
263
|
Pattern-level keys:
|
|
@@ -163,10 +274,29 @@ Pattern-level keys:
|
|
|
163
274
|
| `volume` | number | `1` | Inherited event level. |
|
|
164
275
|
| `waveType` | integer | `SINE` | Inherited waveform. |
|
|
165
276
|
| `pan` | number | `0` | Inherited starting pan. |
|
|
166
|
-
| `panEnd` | number | `
|
|
277
|
+
| `panEnd` | number | same as root `pan` | Inherited ending pan. |
|
|
167
278
|
| `frequencyEnd` | number | `0` | Inherited sweep target. |
|
|
168
279
|
| `lfoFreq` | number | `0` | Inherited vibrato rate. |
|
|
169
280
|
| `lfoDepth` | number | `0` | Inherited vibrato depth. |
|
|
281
|
+
| `noiseRate` | number | `0` | Inherited noise update rate. |
|
|
282
|
+
| `noiseRateEnd` | number | `0` | Inherited noise-rate sweep target. |
|
|
283
|
+
| `lowPassHz` | number | `0` | Inherited low-pass cutoff. |
|
|
284
|
+
| `pulseWidth` | number | `0.5` | Inherited square-wave duty cycle. |
|
|
285
|
+
| `pulseWidthEnd` | number | `0` | Inherited pulse-width sweep target. |
|
|
286
|
+
| `waveTable` | number[] | none | Inherited custom wavetable. |
|
|
287
|
+
| `bitDepth` | integer | `0` | Inherited amplitude quantization. |
|
|
288
|
+
| `decay` | integer | `0` | Inherited decay time. |
|
|
289
|
+
| `sustain` | number | `1` | Inherited sustain level. |
|
|
290
|
+
| `filterType` | string | `none` | Inherited filter type. |
|
|
291
|
+
| `filterHz` | number | `1000` | Inherited cutoff or center. |
|
|
292
|
+
| `filterHzEnd` | number | `0` | Inherited cutoff sweep target. |
|
|
293
|
+
| `filterQ` | number | `0.7071` | Inherited resonance. |
|
|
294
|
+
| `fmRatio` | number | `1` | Inherited modulator ratio. |
|
|
295
|
+
| `fmDepth` | number | `0` | Inherited modulation index. |
|
|
296
|
+
| `fmDepthEnd` | number | none | Inherited index sweep target. |
|
|
297
|
+
| `detune` | number | `0` | Inherited pitch offset in cents. |
|
|
298
|
+
| `spatialX` | number | omitted | Inherited fixed left/right binaural direction. |
|
|
299
|
+
| `spatialY` | number | omitted | Inherited fixed below/above binaural direction. |
|
|
170
300
|
|
|
171
301
|
Step keys:
|
|
172
302
|
|
|
@@ -175,14 +305,123 @@ Step keys:
|
|
|
175
305
|
| `note` | string | Pitch name; exclusive with `frequency` and `rest`. |
|
|
176
306
|
| `frequency` | number | Pitch in Hz; exclusive with `note` and `rest`. |
|
|
177
307
|
| `rest` | Boolean | Must be exactly `true` for a rest. |
|
|
178
|
-
| `duration` | integer |
|
|
308
|
+
| `duration` | integer | Explicit step length in ms. |
|
|
179
309
|
| `noteValue` | string | Step length derived from `bpm`. |
|
|
180
310
|
| `attack`, `release`, `volume`, `waveType` | number | Override the pattern value for this step. |
|
|
181
311
|
| `pan`, `panEnd`, `frequencyEnd`, `lfoFreq`, `lfoDepth` | number | Override motion or vibrato for this step. |
|
|
312
|
+
| `noiseRate`, `noiseRateEnd`, `lowPassHz` | number | Override noise character or filtering. |
|
|
313
|
+
| `pulseWidth`, `pulseWidthEnd` | number | Override square-wave duty cycle or its sweep. |
|
|
314
|
+
| `waveTable` | number[] | Override the custom table for this step. |
|
|
315
|
+
| `bitDepth` | integer | Override amplitude quantization. |
|
|
316
|
+
| `decay`, `sustain` | number | Override the decay stage. |
|
|
317
|
+
| `filterType`, `filterHz`, `filterHzEnd`, `filterQ` | mixed | Override the filter. |
|
|
318
|
+
| `fmRatio`, `fmDepth`, `fmDepthEnd` | number | Override frequency modulation. |
|
|
319
|
+
| `detune` | number | Override the pitch offset in cents. |
|
|
320
|
+
| `spatialX`, `spatialY` | number | Override either inherited coordinate for this step. |
|
|
182
321
|
|
|
183
322
|
Step timing precedence is `duration`, step `noteValue`, pattern `noteValue`,
|
|
184
|
-
pattern `stepDuration`, then 100 ms.
|
|
185
|
-
|
|
323
|
+
pattern `stepDuration`, then 100 ms. The resolved length controls both the voice
|
|
324
|
+
duration and the advance to the next step; there is no independent gate length.
|
|
325
|
+
A step `duration` overrides musical timing rather than shortening a voice inside it. Only one pattern remains pending at a
|
|
326
|
+
time. Starting another one cancels steps that have not fired, and the cancelled
|
|
327
|
+
pattern reports it through the [`patternend` event](#patternend-event).
|
|
328
|
+
|
|
329
|
+
Musical note values and numeric `stepDuration` can resolve to fractional milliseconds; each voice envelope uses the nearest integer millisecond. Explicit step `duration` must be an integer. Each step keeps its resolved duration. Swing changes its scheduled start by `swing` multiplied by that step's resolved duration for even steps (zero-based) and the negative of that offset for odd steps; a negative start is clamped to the call time. Positive swing delays even steps and advances odd ones, so voices can overlap. Pattern `panEnd` defaults to the root `pan`; a step overriding only `pan` still inherits the root ending position. Set both on that step for fixed stereo placement.
|
|
330
|
+
|
|
331
|
+
Acceptance schedules the pattern, but a later native queue overflow can still drop an individual step and log `overflow`; `patternend` describes the schedule, not a guarantee that every step sounded.
|
|
332
|
+
|
|
333
|
+
## Expressive controls
|
|
334
|
+
|
|
335
|
+
These options are opt-in. With none of them present, a note sounds exactly as
|
|
336
|
+
it did without them. They apply to `playTone`, to `playChord` as fixed values,
|
|
337
|
+
and to `playPattern` at the root and on each step.
|
|
338
|
+
|
|
339
|
+
### Decay and sustain
|
|
340
|
+
|
|
341
|
+
After the attack, the level falls from full scale to `sustain` over `decay`
|
|
342
|
+
milliseconds, then holds there until the release begins. With `decay: 0` and
|
|
343
|
+
`sustain: 1` the envelope is the plain attack, full level and release.
|
|
344
|
+
|
|
345
|
+
```text
|
|
346
|
+
level
|
|
347
|
+
1.0 /\
|
|
348
|
+
/ \
|
|
349
|
+
/ \________ sustain
|
|
350
|
+
/ \
|
|
351
|
+
/ \
|
|
352
|
+
+------------------------> time
|
|
353
|
+
attack decay hold release
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
The release scales whatever level the note has reached, so a release that
|
|
357
|
+
begins during the decay stays continuous. `duration` still covers the whole
|
|
358
|
+
event.
|
|
359
|
+
|
|
360
|
+
### Resonant filter
|
|
361
|
+
|
|
362
|
+
`filterType` inserts a 12 dB per octave state-variable filter after the
|
|
363
|
+
one-pole `lowPassHz` filter:
|
|
364
|
+
|
|
365
|
+
| `filterType` | Passes | `filterHz` means |
|
|
366
|
+
| --- | --- | --- |
|
|
367
|
+
| `none` | Everything; the default. | Not allowed. |
|
|
368
|
+
| `lowpass` | Frequencies below the cutoff. | Cutoff. |
|
|
369
|
+
| `highpass` | Frequencies above the cutoff. | Cutoff. |
|
|
370
|
+
| `bandpass` | A band around the center. | Center. |
|
|
371
|
+
|
|
372
|
+
`filterQ` sets the resonance. The default 0.7071 is flat. Higher values add a
|
|
373
|
+
peak at the cutoff: `lowpass` and `highpass` boost it by about `filterQ`, so Q
|
|
374
|
+
10 is roughly 20 dB. `bandpass` keeps unity gain at its center for every Q and
|
|
375
|
+
only gets narrower.
|
|
376
|
+
|
|
377
|
+
`filterHzEnd` moves the cutoff over the whole event. The sweep is exponential:
|
|
378
|
+
equal musical distance per unit of time, so 200 Hz to 3,200 Hz crosses 800 Hz
|
|
379
|
+
at the midpoint. `filterHz` and `filterHzEnd` are held between 20 Hz and
|
|
380
|
+
`sampleRate * 0.45`.
|
|
381
|
+
|
|
382
|
+
### Frequency modulation
|
|
383
|
+
|
|
384
|
+
A positive `fmDepth`, or any `fmDepthEnd`, turns a `SINE` voice into a
|
|
385
|
+
two-operator phase-modulated voice:
|
|
386
|
+
|
|
387
|
+
```text
|
|
388
|
+
output = sin(2 * pi * phase + fmDepth * sin(2 * pi * modulatorPhase))
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
`fmRatio` sets the modulator frequency as a multiple of the carrier's current
|
|
392
|
+
frequency, so pitch sweeps and vibrato keep the ratio. `fmDepth` is the
|
|
393
|
+
modulation index in radians. Index 0 is a plain sine; each increase adds
|
|
394
|
+
sidebands at the carrier frequency plus and minus multiples of the modulator
|
|
395
|
+
frequency. `fmDepthEnd` moves the index linearly over the event. Unlike the
|
|
396
|
+
other `*End` keys, 0 is a real target, so omit the key for no sweep.
|
|
397
|
+
|
|
398
|
+
FM works only on `SINE`. Another waveform is rejected with `fm_requires_sine`.
|
|
399
|
+
|
|
400
|
+
### Detune
|
|
401
|
+
|
|
402
|
+
`detune` shifts the pitch by cents: the frequency is multiplied by
|
|
403
|
+
`2 ^ (cents / 1200)`, so 100 cents is one semitone. It applies to `note`,
|
|
404
|
+
`frequency` and `frequencyEnd`. In a chord, `detunes` supplies one offset per
|
|
405
|
+
pitch instead. The shifted pitch must stay between 1 Hz and the frequency
|
|
406
|
+
ceiling, otherwise the call is rejected with `out_of_range` on `detune` or
|
|
407
|
+
`detunes`.
|
|
408
|
+
|
|
409
|
+
### Rules across keys
|
|
410
|
+
|
|
411
|
+
The rules below use the values a sounding event ends up with. For a pattern
|
|
412
|
+
step, that means the step over the pattern root. A pattern made only of rests is
|
|
413
|
+
not checked.
|
|
414
|
+
|
|
415
|
+
| Reason | Rejected when |
|
|
416
|
+
| --- | --- |
|
|
417
|
+
| `sustain_without_decay` | `sustain` is below 1 and `decay` is 0. |
|
|
418
|
+
| `decay_exceeds_remaining_duration` | `attack` plus `decay` is longer than the event. |
|
|
419
|
+
| `filter_type_required` | `filterHz`, `filterHzEnd` or `filterQ` is set and `filterType` is `none`. |
|
|
420
|
+
| `fm_requires_sine` | `fmDepth` is above 0, or `fmDepthEnd` is set, on a waveform other than `SINE`. |
|
|
421
|
+
| `fm_depth_required` | `fmRatio` is set while FM is off. |
|
|
422
|
+
|
|
423
|
+
For the decay rule, `attack` is the explicit value or the 50 ms default, held
|
|
424
|
+
below `duration`. Set a short `attack` explicitly when the decay is long.
|
|
186
425
|
|
|
187
426
|
## `setVolume(options)`
|
|
188
427
|
|
|
@@ -202,6 +441,35 @@ future pattern steps.
|
|
|
202
441
|
Cancels the pending pattern, stops the native stream and releases its audio
|
|
203
442
|
resources. Call it when the owner closes.
|
|
204
443
|
|
|
444
|
+
## `patternend` event
|
|
445
|
+
|
|
446
|
+
The module emits one event. Listen with `addEventListener`:
|
|
447
|
+
|
|
448
|
+
```javascript
|
|
449
|
+
function onPatternEnd (event) {
|
|
450
|
+
Ti.API.info(event.completed ? 'pattern finished' : 'pattern replaced')
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
synth.addEventListener('patternend', onPatternEnd)
|
|
454
|
+
// when the owner closes:
|
|
455
|
+
synth.removeEventListener('patternend', onPatternEnd)
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The payload is `{ completed: Boolean }`.
|
|
459
|
+
|
|
460
|
+
- Every accepted `playPattern` produces exactly one event. A rejected call
|
|
461
|
+
produces none.
|
|
462
|
+
- `completed: true` arrives at the natural end of the pattern, about 1 ms after
|
|
463
|
+
the sum of its step lengths counted from the call. This reports scheduler completion, not audible silence: swing, callback timing and the binaural FIR tail can leave audio after that event.
|
|
464
|
+
- `completed: false` arrives when a later accepted `playPattern` replaces the
|
|
465
|
+
pattern before it ends.
|
|
466
|
+
- `shutdown()` discards a pending pattern without an event. `stopAll()` does not
|
|
467
|
+
cancel patterns, so it produces none either.
|
|
468
|
+
|
|
469
|
+
Both ports deliver the event asynchronously to the JavaScript runtime, never
|
|
470
|
+
from inside the call that caused it. The timing follows the pattern scheduler,
|
|
471
|
+
so it is not sample-accurate.
|
|
472
|
+
|
|
205
473
|
## Strict validation
|
|
206
474
|
|
|
207
475
|
Unknown keys and invalid values return `false` and produce one deduplicated
|
|
@@ -220,17 +488,115 @@ Common reasons include:
|
|
|
220
488
|
| `non_numeric_or_non_finite` | A numeric field is the wrong type, `NaN` or infinite. |
|
|
221
489
|
| `not_integer` | An integer field contains a fractional number. |
|
|
222
490
|
| `out_of_range` | A number falls outside the documented range. |
|
|
491
|
+
| `missing_required` | A required pitch array or `WAVETABLE` sample array is missing. |
|
|
223
492
|
| `not_string`, `not_array`, `not_boolean` | A field has the wrong container or primitive type. |
|
|
224
493
|
| `invalid_note` | A note name cannot be parsed. |
|
|
225
|
-
| `unsupported_note_value` | A note value is not one of the
|
|
226
|
-
| `
|
|
494
|
+
| `unsupported_note_value` | A note value is not one of the ten supported values. |
|
|
495
|
+
| `unsupported_value` | `mixingProfile`, `audioSession` or `filterType` is not one of the listed strings. |
|
|
496
|
+
| `invalid_sample_count` | `waveTable` has fewer than 2 or more than 64 samples. |
|
|
227
497
|
| `missing_pitch_or_rest` | A pattern step has no pitch and is not a rest. |
|
|
228
498
|
| `conflicting_pitch_fields` | A call or step provides more than one pitch source. |
|
|
499
|
+
| `conflicting_spatial_fields` | Spatial coordinates are combined with stereo pan fields, including inherited pattern values. |
|
|
229
500
|
| `invalid_voice_count` | A chord has fewer than 1 or more than 32 pitches. |
|
|
230
501
|
| `invalid_step_count` | A pattern has fewer than 1 or more than 512 steps. |
|
|
231
|
-
| `too_many_values` | `pans` is longer than the chord. |
|
|
502
|
+
| `too_many_values` | `pans` or `detunes` is longer than the chord. |
|
|
232
503
|
| `attack_greater_or_equal_duration` | An explicit attack leaves no time for the event. |
|
|
233
504
|
| `release_exceeds_remaining_duration` | An explicit release does not fit after attack. |
|
|
505
|
+
| `decay_exceeds_remaining_duration` | `attack` plus `decay` does not fit in the event. |
|
|
506
|
+
| `sustain_without_decay` | `sustain` is below 1 while `decay` is 0. |
|
|
507
|
+
| `filter_type_required` | A filter key is set while `filterType` is `none`. |
|
|
508
|
+
| `fm_requires_sine` | FM is active on a waveform other than `SINE`. |
|
|
509
|
+
| `fm_depth_required` | `fmRatio` is set while FM is off. |
|
|
234
510
|
|
|
235
511
|
Explicit values are not clamped. Read `getDefaults().limits` when accepting
|
|
236
512
|
user input and decide in the app whether to reject or constrain it.
|
|
513
|
+
|
|
514
|
+
## Binaural front-plane positioning
|
|
515
|
+
|
|
516
|
+
`playTone`, `playChord` and `playPattern` (root and steps) accept `spatialX` and `spatialY`, finite numbers from -1 to 1. Supplying either enables headphone binaural processing for that event; the omitted coordinate is 0. Coordinates are fixed for the event. `spatialX: -1` is left, `1` is right, `spatialY: -1` is below, and `1` is above. The center is in front of the listener. The grid spans approximately 60 degrees horizontally and 40 degrees vertically, with interpolated diagonals. A chord shares one position across its voices; each voice is filtered separately.
|
|
517
|
+
|
|
518
|
+
Spatial coordinates cannot be combined with `pan`, `panEnd`, `pans` or `panSpread`, including conflicting fields inherited by a pattern step. Such calls return `false` with `conflicting_spatial_fields`. Omitting both spatial coordinates keeps the stereo renderer. The coordinate limits in `getDefaults().limits` can be used to detect support without starting the engine.
|
|
519
|
+
|
|
520
|
+
```javascript
|
|
521
|
+
var accepted = synth.playTone({
|
|
522
|
+
frequency: 90, waveType: WAVE.NOISE,
|
|
523
|
+
duration: 500, attack: 2, release: 200, volume: 0.3,
|
|
524
|
+
spatialX: 0.6, spatialY: 0.8
|
|
525
|
+
})
|
|
526
|
+
if (!accepted) Ti.API.warn('Spatial explosion rejected')
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
There is no `binaural` Boolean option or percentage parameter on the native API. JavaScript chooses which coordinates to send for each event; `spatialX: 0, spatialY: 0` still enables binaural at the front center. To turn it off, omit both keys. The [integration guide](guide.md#integrate-binaural-in-a-2d-app) shows a switch, amplitude sliders and screen-coordinate conversion without changing synthesis recipes.
|
|
530
|
+
|
|
531
|
+
Listen with stereo headphones. Height perception depends on the listener, headphones and sound spectrum; a low sine tone is a weak test of elevation. This version models a front plane without rear sources, distance attenuation, room reflections or head tracking. The output remains two channels. Filter positions do not follow objects automatically; submit later events at their updated coordinates.
|
|
532
|
+
|
|
533
|
+
## `getStats()`
|
|
534
|
+
|
|
535
|
+
Older beta 1.0.0 packages may lack these methods; use `typeof synth.getStats === 'function'` for feature detection when supporting earlier beta installations.
|
|
536
|
+
|
|
537
|
+
Returns a new snapshot without resetting counters. Available before start and
|
|
538
|
+
after shutdown. Occupancy is the last completed render block; queued commands
|
|
539
|
+
are read at query time. No audio-thread allocation, locks or waiting are added.
|
|
540
|
+
|
|
541
|
+
| Field | Meaning |
|
|
542
|
+
| --- | --- |
|
|
543
|
+
| `activeVoices` | Occupied primary slots, including binaural FIR draining. |
|
|
544
|
+
| `maxVoices` | Configured primary-slot capacity (default 8 before start). |
|
|
545
|
+
| `peakActiveVoices` | Highest primary occupancy since reset/start, including peaks between queries. |
|
|
546
|
+
| `tailVoices` | External fade tails from replacement or `stopAll`, excluding primary FIR drain. |
|
|
547
|
+
| `spatialVoices` | Spatial primary slots, including FIR draining; excludes external tails. |
|
|
548
|
+
| `voiceStealCount` | Primary replacements while all slots are occupied. |
|
|
549
|
+
| `rejectedRequestCount` | Rejected Boolean API requests, counted once per call. |
|
|
550
|
+
| `rejectedRequestsByReason` | `{ validation, engineStopped, queueFull }`; sum equals rejection total. |
|
|
551
|
+
| `droppedPatternStepCount` | Previously accepted pattern steps that could not enter the render queue; cancelled/replaced steps are excluded. |
|
|
552
|
+
| `pendingCommands` | Published entries awaiting the renderer; chord notes count separately, future scheduler steps are excluded. |
|
|
553
|
+
| `sampleRate` | Current native rate, or the last rate after shutdown; 44100 before start. |
|
|
554
|
+
| `callbackFrames` | Frames processed in the most recent callback; 0 before start/after shutdown. |
|
|
555
|
+
| `renderLoad` | Last callback elapsed seconds / (`callbackFrames / sampleRate`); may exceed 1. |
|
|
556
|
+
| `peakRenderLoad` | Maximum callback load in this diagnostic window. |
|
|
557
|
+
| `outputPeakDbFS` | `{ left, right }` peaks of final float output in the latest completed approximately 200 ms window. |
|
|
558
|
+
| `outputRmsDbFS` | `{ left, right }` RMS of that same window. |
|
|
559
|
+
| `limiterReductionDb` | Maximum limiter attenuation in that window, as a positive dB magnitude. |
|
|
560
|
+
| `underrunCount` | Android Oboe accumulated backend xruns since reset/start when supported; otherwise `null`. |
|
|
561
|
+
| `bufferFrames` | Android Oboe output buffer size; otherwise `null`. This is distinct from `callbackFrames`. |
|
|
562
|
+
| `estimatedOutputLatencyMs` | Android timestamp-based stream-to-DAC estimate plus module lookahead, when valid; otherwise `null` (always `null` on this iOS backend). |
|
|
563
|
+
|
|
564
|
+
`renderLoad`, `peakRenderLoad`, `outputPeakDbFS`, `outputRmsDbFS` and
|
|
565
|
+
`limiterReductionDb` are `null` until enabled and measured, and after shutdown.
|
|
566
|
+
Android backend metrics (`underrunCount`, `bufferFrames` and
|
|
567
|
+
`estimatedOutputLatencyMs`) are best-effort readings independent of this
|
|
568
|
+
diagnostic switch. Signal windows use the final output after master volume, limiter and
|
|
569
|
+
clamping, with a silence floor of -120 dBFS. A query does not start or consume a
|
|
570
|
+
window. iOS does not expose a trustworthy underrun count or hardware output
|
|
571
|
+
buffer size through this source-node backend, so those fields are `null`.
|
|
572
|
+
Android preserves supported xrun totals across stream recovery and resets the baseline with `resetStats()`. If a route makes xrun coverage unavailable, the count stays `null` until reset/start restores a measurable window.
|
|
573
|
+
The latency estimate excludes command/scheduler delay and any external DAC/HDMI latency unknown to Android. Timestamp errors, unsupported backends and negative estimates return `null`. It is queried off the audio callback.
|
|
574
|
+
A zero underrun count does not prove clean sound. Render load includes elapsed
|
|
575
|
+
callback work, not total app CPU.
|
|
576
|
+
|
|
577
|
+
## `setStatsEnabled(enabled)`
|
|
578
|
+
|
|
579
|
+
Accepts a Boolean directly, returns Boolean acceptance. Default `false`.
|
|
580
|
+
Enabling starts a new diagnostic window, without restarting audio. Calling it
|
|
581
|
+
again with `true` also starts a fresh window. Disabling hides timing and signal
|
|
582
|
+
fields immediately. Basic counters remain available in either mode. The setting
|
|
583
|
+
persists across shutdown/start, while a new engine starts with fresh counters.
|
|
584
|
+
|
|
585
|
+
## `resetStats()`
|
|
586
|
+
|
|
587
|
+
Returns nothing. Resets accumulated counters and maxima without stopping sounds,
|
|
588
|
+
clearing queued commands or enabling diagnostics. Primary occupancy becomes the
|
|
589
|
+
initial maximum. Render-owned changes take effect at the next callback boundary;
|
|
590
|
+
queries immediately hide stale measurements and report reset accumulated values.
|
|
591
|
+
A new successful start after shutdown also resets statistics. Shutdown clears
|
|
592
|
+
occupancy and commands while retaining accumulated basic totals and maxima.
|
|
593
|
+
|
|
594
|
+
```javascript
|
|
595
|
+
// Optional diagnostics; poll at 5 Hz and stop the timer with the owner.
|
|
596
|
+
if (synth.setStatsEnabled(true)) {
|
|
597
|
+
var statsTimer = setInterval(function () {
|
|
598
|
+
Ti.API.info(JSON.stringify(synth.getStats()))
|
|
599
|
+
}, 200)
|
|
600
|
+
// Later: clearInterval(statsTimer); synth.setStatsEnabled(false)
|
|
601
|
+
}
|
|
602
|
+
```
|