@maccesar/titools 5.2.0 → 5.2.2
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 +82 -0
- package/README.md +83 -72
- package/agents/ti-pro.md +1 -1
- package/commands/ti-audit.md +2 -2
- package/docs/examples.md +91 -0
- package/docs/knowledge-index.md +57 -0
- package/lib/commands/uninstall.js +19 -16
- package/lib/commands/update.js +1 -1
- package/lib/downloader.js +1 -1
- package/lib/utils.js +2 -0
- package/package.json +4 -2
- package/skills/purgetss/references/multi-density-images.md +1 -1
- package/skills/ti-admob-unified/SKILL.md +21 -7
- package/skills/ti-admob-unified/assets/example/app.js +229 -25
- package/skills/ti-admob-unified/assets/example/lib/ads.js +30 -2
- package/skills/ti-admob-unified/assets/example/library-usage.js +33 -1
- package/skills/ti-admob-unified/references/api-reference.md +39 -16
- package/skills/ti-admob-unified/references/banners.md +9 -9
- package/skills/ti-admob-unified/references/contributing.md +14 -9
- package/skills/ti-admob-unified/references/dependencies.md +31 -25
- package/skills/ti-admob-unified/references/fullscreen.md +43 -28
- package/skills/ti-admob-unified/references/game-monetization.md +138 -7
- package/skills/ti-admob-unified/references/getting-started.md +75 -31
- package/skills/ti-admob-unified/references/index.md +5 -2
- package/skills/ti-admob-unified/references/integration.md +8 -6
- package/skills/ti-admob-unified/references/library.md +41 -10
- package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
- package/skills/ti-admob-unified/references/mediation.md +22 -9
- package/skills/ti-admob-unified/references/migration.md +11 -7
- package/skills/ti-admob-unified/references/module-changelog.md +41 -11
- package/skills/ti-admob-unified/references/privacy.md +35 -8
- 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 +28 -31
- package/skills/ti-admob-unified/references/source-notes.md +51 -4
- package/skills/ti-admob-unified/references/source-snapshot.json +249 -45
- package/skills/ti-admob-unified/references/third-party-notices.md +4 -4
- package/skills/ti-game/SKILL.md +23 -6
- package/skills/ti-game/references/api.md +34 -30
- package/skills/ti-game/references/debugging-performance.md +18 -9
- package/skills/ti-game/references/examples.md +103 -20
- package/skills/ti-game/references/input.md +101 -0
- package/skills/ti-game/references/patterns.md +39 -15
- package/skills/ti-game/references/physics.md +80 -52
- package/skills/ti-game/references/project-setup.md +118 -56
- package/skills/ti-game/references/recipes-action.md +107 -8
- package/skills/ti-game/references/recipes-adventure-effects.md +41 -9
- package/skills/ti-game/references/roadmap.md +10 -3
- package/skills/ti-game/references/source-notes.md +81 -0
- package/skills/ti-game/references/tilemaps.md +12 -4
- package/skills/ti-synthengine/SKILL.md +81 -95
- 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 +434 -380
- package/skills/ti-synthengine/references/audio-engine.md +157 -230
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/examples.md +123 -253
- 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 +188 -156
- 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 +423 -599
- package/skills/ti-synthengine/references/recommendations.md +63 -113
- package/skills/ti-synthengine/references/sound-design.md +134 -258
- package/skills/ti-synthengine/references/source-notes.md +93 -105
- package/skills/ti-synthengine/references/source-snapshot.json +379 -32
- package/skills/ti-synthengine/references/stats-performance.md +64 -20
- package/skills/ti-synthengine/references/studio-validation.json +6202 -0
- package/skills/ti-synthengine/references/troubleshooting.md +109 -121
- 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,4 +1,4 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine 1.0.0
|
|
1
|
+
> Source snapshot: official TiSynthEngine 1.0.0 documentation at release source commit `e7cbbe8a6f9ab38088fd8029c7b43cef6501101f` (2026-10-24). Upstream `documentation/api.md` SHA-256: `698d272a85e71692eb5de30586556e53ba19aea88de3b0e583c2d720b150f24b`.
|
|
2
2
|
|
|
3
3
|
# API reference
|
|
4
4
|
|
|
@@ -6,9 +6,7 @@ Module id: `ti.synthengine`
|
|
|
6
6
|
|
|
7
7
|
Public API version: `1.0.0`
|
|
8
8
|
|
|
9
|
-
Android and iOS expose the same
|
|
10
|
-
waveform values and validation behavior. Option objects are strict: an unknown
|
|
11
|
-
key is rejected with `unsupported_option`.
|
|
9
|
+
Android and iOS expose the same nineteen methods, one event and waveform values. Option objects are strict: an unknown key is rejected with `unsupported_option`. Reported metadata and rejection diagnostics have platform differences; this reference does not promise sample-identical rendering between Android and iOS.
|
|
12
10
|
|
|
13
11
|
<!-- TOC-START -->
|
|
14
12
|
## Contents
|
|
@@ -16,32 +14,34 @@ key is rejected with `unsupported_option`.
|
|
|
16
14
|
- [Return values](#return-values)
|
|
17
15
|
- [`getDefaults()`](#getdefaults)
|
|
18
16
|
- [Limits](#limits)
|
|
19
|
-
- [`
|
|
17
|
+
- [`start(options)`](#startoptions)
|
|
18
|
+
- [`noteOn(options)`](#noteonoptions)
|
|
19
|
+
- [`noteOff(options)`](#noteoffoptions)
|
|
20
|
+
- [`updateVoice(options)`](#updatevoiceoptions)
|
|
20
21
|
- [`playTone(options)`](#playtoneoptions)
|
|
22
|
+
- [Native sequence transport](#native-sequence-transport)
|
|
23
|
+
- [`setMasterProcessing(options)`](#setmasterprocessingoptions)
|
|
24
|
+
- [Additional voice modulation](#additional-voice-modulation)
|
|
21
25
|
- [`playChord(options)`](#playchordoptions)
|
|
22
|
-
- [`playPattern(options)`](#playpatternoptions)
|
|
23
26
|
- [Expressive controls](#expressive-controls)
|
|
24
27
|
- [`setVolume(options)`](#setvolumeoptions)
|
|
25
28
|
- [`stopAll()`](#stopall)
|
|
26
29
|
- [`shutdown()`](#shutdown)
|
|
27
|
-
- [`
|
|
30
|
+
- [`sequenceend` event](#sequenceend-event)
|
|
28
31
|
- [Strict validation](#strict-validation)
|
|
29
32
|
- [Binaural front-plane positioning](#binaural-front-plane-positioning)
|
|
30
33
|
- [`getStats()`](#getstats)
|
|
31
34
|
- [`setStatsEnabled(enabled)`](#setstatsenabledenabled)
|
|
32
35
|
- [`resetStats()`](#resetstats)
|
|
36
|
+
- [Routing and voice priority](#routing-and-voice-priority)
|
|
33
37
|
|
|
34
38
|
<!-- TOC-END -->
|
|
35
39
|
|
|
36
40
|
## Return values
|
|
37
41
|
|
|
38
|
-
`
|
|
39
|
-
Boolean. `true` means the request was accepted. `false` means validation
|
|
40
|
-
failed, the engine was unavailable, or the native command queue was full.
|
|
42
|
+
`start`, `playTone`, `playChord`, `noteOn`, `noteOff`, `updateVoice`, `setVolume`, `setTempo`, `updateSequence`, `stopSequence` and `setMasterProcessing` return a Boolean. `playSequence` returns a positive sequence identifier, or 0 when rejected. `true` means the request was accepted. `false` means validation failed, the engine was unavailable, or the native command queue was full.
|
|
41
43
|
|
|
42
|
-
`setStatsEnabled` also returns Boolean acceptance.
|
|
43
|
-
`stopAll`, `resetStats` and `shutdown` do not return a value. `getDefaults` returns the
|
|
44
|
-
contract object described below.
|
|
44
|
+
`setStatsEnabled` also returns Boolean acceptance. `stopAll`, `resetStats` and `shutdown` do not return a value. `getDefaults` returns the contract object described below. `getStats` returns a new statistics snapshot. `getSequenceState` returns the applied sequence state or `null`.
|
|
45
45
|
|
|
46
46
|
## `getDefaults()`
|
|
47
47
|
|
|
@@ -51,13 +51,19 @@ Returns the installed contract without starting the audio stream.
|
|
|
51
51
|
var contract = synth.getDefaults()
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
The result has this shape:
|
|
54
|
+
The shared result has this shape; Android adds the platform metadata described below:
|
|
55
55
|
|
|
56
56
|
```javascript
|
|
57
57
|
{
|
|
58
58
|
apiVersion: '1.0.0',
|
|
59
59
|
defaults: {
|
|
60
60
|
maxVoices: 8,
|
|
61
|
+
maxSequences: 1,
|
|
62
|
+
timeUnit: 'beats',
|
|
63
|
+
gate: 0.8,
|
|
64
|
+
loop: false,
|
|
65
|
+
bus: 'effects',
|
|
66
|
+
priority: 0,
|
|
61
67
|
mixingProfile: 'conservative',
|
|
62
68
|
audioSession: 'playback',
|
|
63
69
|
frequency: 440,
|
|
@@ -81,7 +87,7 @@ The result has this shape:
|
|
|
81
87
|
panSpread: 0,
|
|
82
88
|
bpm: 120,
|
|
83
89
|
swing: 0,
|
|
84
|
-
stepDuration:
|
|
90
|
+
stepDuration: 0.25,
|
|
85
91
|
decay: 0,
|
|
86
92
|
sustain: 1,
|
|
87
93
|
filterType: 'none',
|
|
@@ -95,6 +101,7 @@ The result has this shape:
|
|
|
95
101
|
limits: { /* ranges listed below */ },
|
|
96
102
|
mixingProfiles: ['conservative', 'raw', 'balanced', 'speakerSafe'],
|
|
97
103
|
audioSessions: ['playback', 'ambient'],
|
|
104
|
+
buses: ['music', 'effects', 'ui', 'ambience'],
|
|
98
105
|
filterTypes: ['none', 'lowpass', 'highpass', 'bandpass'],
|
|
99
106
|
waveTypes: {
|
|
100
107
|
SINE: 0,
|
|
@@ -107,240 +114,332 @@ The result has this shape:
|
|
|
107
114
|
}
|
|
108
115
|
```
|
|
109
116
|
|
|
110
|
-
`fmDepthEnd`, `detunes`, `spatialX` and `spatialY` have no reported default
|
|
111
|
-
|
|
112
|
-
the
|
|
117
|
+
`fmDepthEnd`, `detunes`, `spatialX` and `spatialY` have no reported default: their presence changes how the call is interpreted. 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 reported fallback is 19,845 Hz. Read the object again after `start()` if the precise device limit matters.
|
|
118
|
+
|
|
119
|
+
Android additionally reports `capabilities: { nativeSequence: true, masterProcessing: true, modulations: true }` and `limits.maxSequences`, `limits.priority`, `limits.effectSend` and `limits.stepDurationMs`. The reviewed iOS result omits those metadata keys while implementing the corresponding methods and option ranges. Detect the required methods instead of requiring an Android-only flag. `limits.stepDuration` is in beats on both platforms; for millisecond sequences the accepted range is `4000 / sampleRate` through 60,000.
|
|
113
120
|
|
|
114
121
|
## Limits
|
|
115
122
|
|
|
116
123
|
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.
|
|
117
124
|
|
|
118
|
-
| Field
|
|
119
|
-
|
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
| `
|
|
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. |
|
|
125
|
+
| Field | Minimum | Maximum | Unit or rule |
|
|
126
|
+
| --------------------------- | --------: | ------------------: | ------------------------------------------------------------------------- |
|
|
127
|
+
| `maxSequences` | 1 | 4 | preallocated sequence capacity |
|
|
128
|
+
| `priority` | 0 | 3 | integer admission priority |
|
|
129
|
+
| `effectSend` | 0 | 1 | explicit processed share |
|
|
130
|
+
| `maxVoices` | 1 | 32 | integer voices |
|
|
131
|
+
| `frequency` | 1 | `sampleRate * 0.45` | Hz |
|
|
132
|
+
| `duration` | 1 | 60,000 | integer ms, including timed-event duration |
|
|
133
|
+
| `attack` | 0 | 2,000 | integer ms; must be less than duration |
|
|
134
|
+
| `release` | 0 | 4,000 | integer ms; must fit after attack |
|
|
135
|
+
| `volume` | 0 | 2 | event or master multiplier |
|
|
136
|
+
| `waveType` | 0 | 5 | integer waveform value |
|
|
137
|
+
| `sequenceSteps` | 1 | 512 | object steps |
|
|
138
|
+
| `pan`, `panEnd`, `pans[]` | -1 | 1 | left to right |
|
|
139
|
+
| `spatialX`, `spatialY` | -1 | 1 | initial binaural direction; omitted unless enabled |
|
|
140
|
+
| `panSpread` | 0 | 1 | chord stereo width |
|
|
141
|
+
| `frequencyEnd` | 0 | `sampleRate * 0.45` | Hz; values below 1 disable the sweep |
|
|
142
|
+
| `lfoFreq` | 0 | 20,000 | Hz; 0 disables vibrato |
|
|
143
|
+
| `lfoDepth` | 0 | 20,000 | Hz |
|
|
144
|
+
| `noiseRate`, `noiseRateEnd` | 0 | `sampleRate * 0.45` | Hz; 0 selects white noise or disables the sweep |
|
|
145
|
+
| `lowPassHz` | 0 | `sampleRate * 0.45` | Hz; 0 disables the filter |
|
|
146
|
+
| `pulseWidth` | 0.05 | 0.95 | fraction of a square-wave cycle |
|
|
147
|
+
| `pulseWidthEnd` | 0 or 0.05 | 0.95 | 0 disables the sweep |
|
|
148
|
+
| `waveTable` | 2 samples | 64 samples | each value must be from -1 to 1; count range is `limits.waveTableSamples` |
|
|
149
|
+
| `bitDepth` | 0 or 2 | 16 | integer bits; 0 disables quantization |
|
|
150
|
+
| `bpm` | 20 | 300 | beats per minute |
|
|
151
|
+
| `swing` | -0.75 | 0.75 | alternating timing offset |
|
|
152
|
+
| `stepDuration` | `20 / sampleRate` | 300 | beats; returned by both platforms |
|
|
153
|
+
| `stepDurationMs` | `4000 / sampleRate` | 60,000 | ms; returned by Android only, accepted in ms sequences on both |
|
|
154
|
+
| `decay` | 0 | 4,000 | integer ms; `attack` plus `decay` must fit in the event |
|
|
155
|
+
| `sustain` | 0 | 1 | level held after the decay |
|
|
156
|
+
| `filterHz` | 20 | `sampleRate * 0.45` | Hz |
|
|
157
|
+
| `filterHzEnd` | 0 or 20 | `sampleRate * 0.45` | Hz; 0 disables the sweep |
|
|
158
|
+
| `filterQ` | 0.5 | 10 | resonance |
|
|
159
|
+
| `fmRatio` | 0.1 | 16 | modulator frequency divided by carrier frequency |
|
|
160
|
+
| `fmDepth`, `fmDepthEnd` | 0 | 10 | modulation index in radians |
|
|
161
|
+
| `detune`, `detunes[]` | -1200 | 1200 | cents |
|
|
162
|
+
|
|
163
|
+
## `start(options)`
|
|
164
|
+
|
|
165
|
+
Starts the native stream. Calling it again while the engine is running returns `true` without rebuilding the stream.
|
|
166
|
+
|
|
167
|
+
| Key | Type | Default | Description |
|
|
168
|
+
| --------------- | ------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
169
|
+
| `maxSequences` | integer | `1` | Preallocated simultaneous sequence capacity, from 1 to 4. |
|
|
170
|
+
| `maxVoices` | integer | `8` | Size of the voice pool, from 1 to 32. |
|
|
171
|
+
| `mixingProfile` | string | `conservative` | `conservative`, `balanced`, `speakerSafe` or `raw`. Matching is case-insensitive. |
|
|
172
|
+
| `audioSession` | string | `playback` | iOS session category: `playback` or `ambient`. Matching is case-insensitive. Android validates the value and does not change its output. |
|
|
168
173
|
|
|
169
174
|
Options are validated even when the engine is already running; a second call with invalid options still returns `false`.
|
|
170
175
|
|
|
171
|
-
The profile, voice capacity and audio session stay fixed for the session. Call
|
|
172
|
-
|
|
176
|
+
The profile, voice capacity and audio session stay fixed for the session. Call `shutdown()` before starting again with different values.
|
|
177
|
+
|
|
178
|
+
On iOS, `playback` ignores the silent switch and interrupts other apps' audio. `ambient` is muted by the silent switch and mixes with other apps' audio. The module applies the category again when it resumes after a route change.
|
|
179
|
+
|
|
180
|
+
## `noteOn(options)`
|
|
181
|
+
|
|
182
|
+
Starts a sustained native voice. Detect `typeof synth.noteOn === 'function'`, `noteOff` and `updateVoice` before enabling their controls; `apiVersion` is `1.0.0`. There is no duration limit and no JavaScript timer holds the note.
|
|
183
|
+
|
|
184
|
+
| Field | Rule |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| `voiceId` | Required positive integer, 1-2,147,483,647, supplied by the app. |
|
|
187
|
+
| `frequency`, `note` | Optional mutually exclusive pitch; default 440 Hz, same ranges as playTone. |
|
|
188
|
+
| `attack`, `decay`, `sustain`, `release` | ADSR configuration for this attack; release begins only on noteOff. Same individual ranges as playTone, without a duration budget. |
|
|
189
|
+
| `volume` | Initial voice level, 0-2; default 1. |
|
|
190
|
+
| `waveType` | One of the six waveTypes; fixed for this attack. |
|
|
191
|
+
| `pan` | Initial stereo position, −1…1. Conflicts with spatial coordinates. |
|
|
192
|
+
| `lfoFreq`, `lfoDepth` | Initial vibrato, 0-20,000 Hz; default 0. |
|
|
193
|
+
| `noiseRate`, `lowPassHz` | Initial noise clock / one-pole cutoff, 0 through the frequency maximum; default 0. |
|
|
194
|
+
| `pulseWidth` | Initial square-wave duty cycle, 0.05-0.95; default 0.5. |
|
|
195
|
+
| `waveTable` | Fixed table of 2-64 samples in −1…1; required for WAVETABLE. |
|
|
196
|
+
| `bitDepth` | Fixed amplitude quantization, 0 or integer 2-16; default 0. |
|
|
197
|
+
| `filterType` | Fixed none/lowpass/highpass/bandpass; default none. |
|
|
198
|
+
| `filterHz`, `filterQ` | Initial cutoff and Q; a non-none filter type is required. |
|
|
199
|
+
| `fmRatio`, `fmDepth` | Initial sine FM; same rules and ranges as playTone. |
|
|
200
|
+
| `detune` | Initial pitch offset in cents, −1200…1200; default 0. |
|
|
201
|
+
| `spatialX`, `spatialY` | Initial binaural position in −1…1; omitted for stereo. Compatible held-position updates are live. |
|
|
202
|
+
| `modulations` | Up to two native LFOs; see Additional voice modulation. |
|
|
203
|
+
| `bus` | Category: music, effects (default), ui or ambience. |
|
|
204
|
+
| `effectSend` | Optional explicit processed share 0-1, replacing category selection. |
|
|
205
|
+
| `priority` | Integer admission priority 0-3, default 0. |
|
|
206
|
+
|
|
207
|
+
No `duration` or temporal `*End` targets are accepted. Source, table, filter type and envelope configuration require a new attack; compatible position controls can be updated. Other initial defaults and cross-field validation rules match `playTone`.
|
|
208
|
+
|
|
209
|
+
Sustained voices apply a minimum 3 ms attack and release edge to prevent a hard gate when the requested time is 0-2 ms. Longer ADSR times are unchanged. Timed tones retain their duration-budget envelope behavior; sequence notes release from their current envelope level at their gate/length deadline.
|
|
210
|
+
|
|
211
|
+
The app supplies IDs and should never reuse one during its session. Reusing an active ID explicitly replaces that attack, snapshots a 6 ms fade tail and uses at least 6 ms attack for the replacement. Within priority-eligible candidates in a full pool, release voices are replaced first, then the oldest attack. Timed-only events retain their existing replacement policy and sound. A former ID cannot control its replacement.
|
|
212
|
+
|
|
213
|
+
Repeating a public sustained note can inherit the oscillator phase and pre-envelope filter history of the newest compatible note still releasing at the same pitch. This prevents a fresh phase reset from cancelling its release tail. Each note keeps its own ID, envelope, gain and release. Compatibility requires stable matching oscillator, vibrato and filter settings; changing parameters or the two optional LFOs retain independent starts. Simultaneously held unisons, native transport notes and timed calls retain their existing phase policy.
|
|
214
|
+
|
|
215
|
+
## `noteOff(options)`
|
|
216
|
+
|
|
217
|
+
| Field | Rule |
|
|
218
|
+
| --- | --- |
|
|
219
|
+
| `voiceId` | Required positive integer, 1-2,147,483,647. |
|
|
220
|
+
|
|
221
|
+
Begins release from the current level, including during attack or decay. Repeated release requests never restart or lengthen a release. An ended or stolen ID is a no-op. `true` reports validation and queue publication, not proof of an audible voice. On `false` from a full queue, an instrument should retry twice at 16 ms intervals, then shut down and report failure if release still cannot be published.
|
|
222
|
+
|
|
223
|
+
## `updateVoice(options)`
|
|
224
|
+
|
|
225
|
+
| Field | Rule |
|
|
226
|
+
| --- | --- |
|
|
227
|
+
| `voiceId` | Required positive integer, 1-2,147,483,647. |
|
|
228
|
+
| `frequency`, `note` | Mutually exclusive target pitch. Same frequency/note ranges as playTone. |
|
|
229
|
+
| `volume` | Target voice level, 0-2. |
|
|
230
|
+
| `filterHz`, `filterQ` | Target cutoff/Q; only for a voice that started with a resonant filter. |
|
|
231
|
+
| `lfoFreq`, `lfoDepth` | Target vibrato rate/deviation, 0-20,000 Hz. |
|
|
232
|
+
| `pulseWidth` | Target duty cycle, 0.05-0.95; only for a square voice. |
|
|
233
|
+
| `fmRatio`, `fmDepth` | Target FM ratio 0.1-16 / depth 0-10; only for a sine voice. |
|
|
234
|
+
| `modulations` | Replace up to two native LFOs; empty array disables them while preserving phase. |
|
|
235
|
+
| `noiseRate`, `lowPassHz` | Live noise clock/one-pole cutoff; 0 means white noise/bypass. |
|
|
236
|
+
| `pan` | Target stereo position for a voice started without spatial coordinates. |
|
|
237
|
+
| `spatialX`, `spatialY` | Target position for a spatial held voice; omitted coordinate stays unchanged. |
|
|
238
|
+
| `effectSend` | Explicit processed share, 0-1, overriding category selection. |
|
|
239
|
+
|
|
240
|
+
At least one change is required. An incompatible change rejects the entire request when the ID is known; unknown/ended IDs accept publication as a no-op. The callback also checks source compatibility before applying a command. Changed continuous parameters ramp linearly from their current values over 10 ms. New targets retarget that ramp without restarting oscillator phase or the envelope. Unchanged parameters retain their current ramps. This also works during release.
|
|
241
|
+
|
|
242
|
+
Pitch updates are absolute: an initial `detune` is already included in the note's starting frequency. To edit tuning later, compute the desired effective frequency in the app and send `frequency`. There is no `detune` update field. Filter type, ADSR, quantization and wavetable require a new attack. Noise clock, low-pass cutoff, send and compatible stereo/spatial position support live 10 ms updates.
|
|
243
|
+
|
|
244
|
+
```javascript
|
|
245
|
+
if (typeof synth.noteOn !== 'function' || typeof synth.noteOff !== 'function' ||
|
|
246
|
+
typeof synth.updateVoice !== 'function') throw new Error('Sustained voices unavailable')
|
|
247
|
+
if (!synth.noteOn({ voiceId: 1, note: 'C4', attack: 8, decay: 180, sustain: 0.6,
|
|
248
|
+
release: 300, filterType: 'lowpass', filterHz: 1200, volume: 0.25 })) {
|
|
249
|
+
throw new Error('Note rejected')
|
|
250
|
+
}
|
|
251
|
+
synth.updateVoice({ voiceId: 1, filterHz: 3600 })
|
|
252
|
+
synth.noteOff({ voiceId: 1 })
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Use `stopAll()` for panic: it stops every sequence, fades sounding voices and invalidates effect histories. Owner close should call `shutdown()` after cancelling app-owned UI/retry timers.
|
|
173
256
|
|
|
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.
|
|
177
257
|
|
|
178
258
|
## `playTone(options)`
|
|
179
259
|
|
|
180
260
|
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.
|
|
181
261
|
|
|
182
|
-
| Key
|
|
183
|
-
|
|
|
184
|
-
| `frequency`
|
|
185
|
-
| `note`
|
|
186
|
-
| `duration`
|
|
187
|
-
| `attack`
|
|
188
|
-
| `release`
|
|
189
|
-
| `volume`
|
|
190
|
-
| `waveType`
|
|
191
|
-
| `pan`
|
|
192
|
-
| `panEnd`
|
|
193
|
-
| `frequencyEnd`
|
|
194
|
-
| `lfoFreq`
|
|
195
|
-
| `lfoDepth`
|
|
196
|
-
| `noiseRate`
|
|
197
|
-
| `noiseRateEnd`
|
|
198
|
-
| `lowPassHz`
|
|
199
|
-
| `pulseWidth`
|
|
200
|
-
| `pulseWidthEnd` | number
|
|
201
|
-
| `waveTable`
|
|
202
|
-
| `bitDepth`
|
|
203
|
-
| `decay`
|
|
204
|
-
| `sustain`
|
|
205
|
-
| `filterType`
|
|
206
|
-
| `filterHz`
|
|
207
|
-
| `filterHzEnd`
|
|
208
|
-
| `filterQ`
|
|
209
|
-
| `fmRatio`
|
|
210
|
-
| `fmDepth`
|
|
211
|
-
| `fmDepthEnd`
|
|
212
|
-
| `detune`
|
|
213
|
-
| `spatialX`
|
|
214
|
-
| `spatialY`
|
|
262
|
+
| Key | Type | Default | Description |
|
|
263
|
+
| --------------- | -------- | ------------: | ------------------------------------------------------------------------------------------ |
|
|
264
|
+
| `frequency` | number | `440` | Starting frequency in Hz. Cannot be combined with `note`. |
|
|
265
|
+
| `note` | string | none | Note name. Cannot be combined with `frequency`. |
|
|
266
|
+
| `duration` | integer | `500` | Total event length in ms. |
|
|
267
|
+
| `attack` | integer | `50` | Fade-in time in ms. |
|
|
268
|
+
| `release` | integer | `50` | Fade-out time in ms. |
|
|
269
|
+
| `volume` | number | `1` | Event level multiplier. |
|
|
270
|
+
| `waveType` | integer | `SINE` | One of the six waveform values. |
|
|
271
|
+
| `pan` | number | `0` | Starting stereo position. |
|
|
272
|
+
| `panEnd` | number | same as `pan` | Ending stereo position; omission keeps the position fixed. |
|
|
273
|
+
| `frequencyEnd` | number | `0` | Ending frequency; values below 1 keep the starting pitch. |
|
|
274
|
+
| `lfoFreq` | number | `0` | Vibrato rate in Hz. |
|
|
275
|
+
| `lfoDepth` | number | `0` | Vibrato depth in Hz. |
|
|
276
|
+
| `noiseRate` | number | `0` | Noise update rate; 0 produces a fresh value per sample. |
|
|
277
|
+
| `noiseRateEnd` | number | `0` | Ending noise update rate; 0 disables the sweep. |
|
|
278
|
+
| `lowPassHz` | number | `0` | Per-voice low-pass cutoff; 0 bypasses it. |
|
|
279
|
+
| `pulseWidth` | number | `0.5` | Duty cycle for `SQUARE`. |
|
|
280
|
+
| `pulseWidthEnd` | number | `0` | Ending duty cycle; 0 disables the sweep. |
|
|
281
|
+
| `waveTable` | number[] | none | Required for `WAVETABLE`; 2 to 64 normalized samples. |
|
|
282
|
+
| `bitDepth` | integer | `0` | Amplitude quantization from 2 to 16 bits; 0 disables it. |
|
|
283
|
+
| `decay` | integer | `0` | Milliseconds to fall from full level to `sustain` after the attack. |
|
|
284
|
+
| `sustain` | number | `1` | Level held after the decay, from 0 to 1. |
|
|
285
|
+
| `filterType` | string | `none` | `none`, `lowpass`, `highpass` or `bandpass`. Case-insensitive. |
|
|
286
|
+
| `filterHz` | number | `1000` | Cutoff, or center for `bandpass`, in Hz. |
|
|
287
|
+
| `filterHzEnd` | number | `0` | Ending cutoff; 0 disables the sweep. |
|
|
288
|
+
| `filterQ` | number | `0.7071` | Resonance, from 0.5 to 10. |
|
|
289
|
+
| `fmRatio` | number | `1` | Modulator frequency as a multiple of the carrier. |
|
|
290
|
+
| `fmDepth` | number | `0` | Modulation index; 0 leaves FM off unless `fmDepthEnd` is set. |
|
|
291
|
+
| `fmDepthEnd` | number | none | Ending index. Omit it for no sweep; 0 is a valid target. |
|
|
292
|
+
| `detune` | number | `0` | Pitch offset in cents, applied to `frequency` and `frequencyEnd`. |
|
|
293
|
+
| `spatialX` | number | omitted | Fixed left/right binaural direction; see [positioning](#binaural-front-plane-positioning). |
|
|
294
|
+
| `spatialY` | number | omitted | Fixed below/above binaural direction. |
|
|
295
|
+
| `bus` | string | `effects` | Category: music, effects, ui or ambience. |
|
|
296
|
+
| `effectSend` | number | omitted | Explicit processed share 0-1; replaces category selection. |
|
|
297
|
+
| `priority` | integer | `0` | Admission priority 0-3; higher voices are protected. |
|
|
215
298
|
|
|
216
299
|
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
300
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
301
|
+
At this source pin, the iOS timed tone/chord command builder converts accepted
|
|
302
|
+
zero attack/release values to at least one sample. Android retains zero samples.
|
|
303
|
+
This differs from the shared validator accepting zero; do not promise identical
|
|
304
|
+
zero-edge rendering. Held notes have the separately documented 3 ms minimum.
|
|
305
|
+
|
|
306
|
+
The controls from `decay` to `detune` are described in [Expressive controls](#expressive-controls), including the rules that tie them together.
|
|
307
|
+
|
|
308
|
+
## Native sequence transport
|
|
309
|
+
|
|
310
|
+
`start({ maxSequences: 1 })` preallocates capacity for 1-4 independent sequences; the default is 1. All sequences, timed sounds and held notes share one renderer and the same pool of at most 32 voices. No sequence or category reserves voices.
|
|
311
|
+
|
|
312
|
+
### `setTempo({ bpm })`
|
|
313
|
+
|
|
314
|
+
Returns Boolean acceptance. BPM is 20-300, initially 120. The change applies at the next audio block and preserves musical position. It controls beat-based sequences and explicitly synchronized delays/LFOs. Millisecond sequences and free-running effects retain their timing.
|
|
315
|
+
|
|
316
|
+
### `playSequence(options)`
|
|
317
|
+
|
|
318
|
+
Returns a positive `sequenceId` on accepted atomic publication, or 0 on rejection. Identifiers never repeat during the module's lifetime, including across shutdown/start. Publication acceptance precedes application by the callback. Capacity exhaustion, unavailable banks or invalid options reject the complete operation.
|
|
319
|
+
|
|
320
|
+
| Root field | Rule |
|
|
321
|
+
| --- | --- |
|
|
322
|
+
| `steps` | Required array of 1-512 step objects. |
|
|
323
|
+
| `timeUnit` | `beats` (default) or `ms`, fixed for this sequence. One beat is a quarter note. |
|
|
324
|
+
| `stepDuration` | Default 0.25 beats or 100 ms; maximum 300 beats or 60,000 ms. Minimum is four output samples at the highest tempo and maximum swing: `20 / sampleRate` beats or `4000 / sampleRate` ms. |
|
|
325
|
+
| `gate` | 0.01-1, default 0.8; fraction of the effective interval before note release begins. |
|
|
326
|
+
| `loop` | Boolean, default false. |
|
|
327
|
+
| `swing` | −0.75…0.75, default 0. For intervals A/B, shifts `swing * min(A, B)` from the second interval to the first, preserving the pair's total length. Applies at the next pair. |
|
|
328
|
+
| `mode` | `normal` (default), `up`, `down` or `updown`. Arpeggios traverse sounding steps in pitch order; rests participate only in normal mode. |
|
|
329
|
+
| `tuning` | A4 frequency, 220-880 Hz, default 440. |
|
|
330
|
+
| `detune` | MIDI pitch offset in cents, −1200…1200, default 0. |
|
|
331
|
+
| `voice` | Inherited sound settings, listed below. |
|
|
332
|
+
| `bus`, `effectSend`, `priority` | Inherited routing/admission settings; may also appear in `voice` or on each step. |
|
|
333
|
+
|
|
334
|
+
Each step contains exactly one representation: `notes` (1-32 MIDI integers, 12-119), `frequencies` (1-32 frequencies in Hz), or `rest: true`. Duplicate pitches produce unison voices. Optional `duration` changes the interval until the next attack; `length` explicitly sets note duration until release begins and replaces gate for that step. Both use the sequence's time unit (at most 300 beats or 60,000 ms). Note length is positive, at least 0.000001; intervals use the four-sample scheduling minimum. Notes can overlap later attacks. Step `gate` overrides root gate. `overrides` merges shaping over root `voice`.
|
|
335
|
+
|
|
336
|
+
The `voice` and `overrides` dictionaries accept `attack`, `decay`, `sustain`, `release`, `volume`, `waveType`, `pan`, `panEnd`, `frequencyEnd`, `lfoFreq`, `lfoDepth`, `noiseRate`, `noiseRateEnd`, `lowPassHz`, `pulseWidth`, `pulseWidthEnd`, `waveTable`, `bitDepth`, `filterType`, `filterHz`, `filterHzEnd`, `filterQ`, `fmRatio`, `fmDepth`, `fmDepthEnd`, `spatialX`, `spatialY`, `modulations`, `bus`, `effectSend` and `priority`. Individual tone parameter ranges and source/filter compatibility rules apply. ADSR remains in milliseconds and has no step-duration budget. Sweeps traverse the note's length and hold their destination throughout release. Chord compensation is fixed at attack; later notes never rescale existing voices.
|
|
337
|
+
|
|
338
|
+
### `updateSequence({ sequenceId, ...changes })`
|
|
339
|
+
|
|
340
|
+
Returns Boolean publication acceptance. A positive known identifier is required. Root/voice edits merge with the last accepted configuration; `steps` replaces the entire array. Structural edits enter at the next step and swing at the next pair. Shortening a traversal adjusts its cursor. The time unit cannot change. An update cannot resume a retired sequence. Each sequence has three preallocated banks; retry rejected publications outside the callback.
|
|
341
|
+
|
|
342
|
+
### `stopSequence({ sequenceId })`
|
|
343
|
+
|
|
344
|
+
Returns Boolean acceptance. Cancels future attacks and releases only that sequence's voices at the next block, independently of public command-queue capacity. Effect tails can finish. An unknown or retired identifier returns false.
|
|
345
|
+
|
|
346
|
+
### `getSequenceState({ sequenceId })`
|
|
347
|
+
|
|
348
|
+
Returns `{ sequenceId, playing, step, beat, position, bpm, revision }`, or null for an unknown/retired identifier. `step` is the original zero-based index (−1 before the first attack). Position is measured in the sequence's units; `revision` is the applied configuration. Polling serves presentation and does not drive timing.
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
synth.setTempo({ bpm: 120 })
|
|
352
|
+
var musicId = synth.playSequence({ loop: true, bus: 'music',
|
|
353
|
+
voice: { volume: 0.2 },
|
|
354
|
+
steps: [{ notes: [60, 64, 67] }, { rest: true }, { frequencies: [440], length: 0.5 }]
|
|
355
|
+
})
|
|
356
|
+
if (musicId === 0) throw new Error('Sequence publication rejected')
|
|
357
|
+
synth.updateSequence({ sequenceId: musicId, swing: 0.12 })
|
|
358
|
+
synth.setTempo({ bpm: 137 })
|
|
359
|
+
var state = synth.getSequenceState({ sequenceId: musicId })
|
|
360
|
+
synth.stopSequence({ sequenceId: musicId })
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## `setMasterProcessing(options)`
|
|
364
|
+
|
|
365
|
+
Selected audio follows fixed input gain → chorus → ping-pong delay → reverb → master volume → optional saturation. Dry audio receives the same global input/master gains, then recombines before one final lookahead limiter. Independent notes never trigger voice-count normalization. The limiter remains the final protection.
|
|
366
|
+
|
|
367
|
+
Requires a running engine and returns Boolean acceptance. Partial updates merge into the last accepted processing configuration, validate the complete result and publish atomically. Continuous controls ramp over 10 ms. Unknown fields, numeric booleans and non-finite values reject the whole edit. Detect method presence before use.
|
|
368
|
+
|
|
369
|
+
| Section | Fields and initial values |
|
|
370
|
+
| --- | --- |
|
|
371
|
+
| Root | `applyTo`: array of `music`, `effects`, `ui`, `ambience` initially; empty selects none. |
|
|
372
|
+
| Root | `inputGain`: 0-4, initially 1. |
|
|
373
|
+
| `chorus` | `enabled`: false; `rate`: 0.05-5 Hz, initially 0.8; `depth`: 0-1, initially 0.4; `mix`: 0-1, initially 0.25. Stereo interpolated delay with offset modulation phases. |
|
|
374
|
+
| `delay` | `enabled`: false; `timeMs`: 20-2000, initially 250; `sync`: off; `feedback`: 0-0.85, initially 0.3; `mix`: 0-1, initially 0.25. Ping-pong feedback and crossfade between changed read positions, up to four seconds reserved. |
|
|
375
|
+
| `reverb` | `enabled`: false; `decay`: 0.2-8 seconds, initially 1.5; `damping`: 0-1, initially 0.5; `mix`: 0-1, initially 0.2. Stereo damped comb/allpass Schroeder network. |
|
|
376
|
+
| `saturation` | `enabled`: false; `drive`: 1-4, initially 1; `mix`: 0-1, initially 1. Creative soft waveshaping that changes timbre. |
|
|
377
|
+
|
|
378
|
+
Delay sync accepts `off`, `quarter`, `eighth`, `sixteenth`, `quarterTriplet`, `eighthTriplet` or `sixteenthTriplet`. Sync follows the native transport BPM, initially 120. Disabling an effect gradually closes its input and drains its tail, then restores exact bypass. Tails are bounded to 30 seconds with a final fade. `stopAll()` performs panic: it fades voices, stops the new transport and invalidates effect histories in bounded work. `shutdown()` stops the stream and frees buffers outside the callback.
|
|
379
|
+
|
|
380
|
+
Processing uses one shared chain for the selected categories. `applyTo` is an array drawn from `music`, `effects`, `ui` and `ambience`; default all four. Each event or held voice uses `bus: 'effects'` by default. Optional `effectSend: 0…1` overrides category selection for that voice. Input gain and master volume remain global. Selected audio receives chorus, delay, reverb and optional saturation, then recombines with dry audio before one final limiter. Send changes ramp over 10 ms.
|
|
381
|
+
|
|
382
|
+
Defaults keep all effects and saturation disabled, input gain at unity, and preserve the earlier render exactly. Enabling saturation is an explicit sound-design choice. Output protection is still required with dense voices and effect feedback.
|
|
383
|
+
|
|
384
|
+
## Additional voice modulation
|
|
385
|
+
|
|
386
|
+
`noteOn` and `updateVoice` accept `modulations`, an array of up to two LFO objects. An empty array disables them. Omission on update preserves the previous configuration. Each object accepts only these fields:
|
|
387
|
+
|
|
388
|
+
| Field | Rule |
|
|
389
|
+
| --- | --- |
|
|
390
|
+
| `wave` | `sine` (default) or `triangle`. |
|
|
391
|
+
| `target` | Required: `filterHz`, `pulseWidth` or `fmDepth`. Requires an active resonant filter, square source or sine source respectively. An explicit FM ratio with zero base depth is permitted when an FM-target LFO is enabled. |
|
|
392
|
+
| `rate` | 0-20 Hz, default 1. Zero permits a stationary free-running phase. |
|
|
393
|
+
| `depth` | 0-1, default 0; scales up to four cutoff octaves, 0.45 PWM offset or ten FM-index units. Two contributions sum with safe clamping. |
|
|
394
|
+
| `sync` | Same musical values as delay; default off. Synced rates follow native tempo (up to 30 Hz at 300 BPM in sixteenth-note triplets). |
|
|
395
|
+
|
|
396
|
+
Edits preserve oscillator/LFO phases. Continuous modulation contributions ramp over 10 ms; cutoff stays within 20 Hz and the frequency maximum, PWM within 0.05-0.95, FM index within 0-10. Initial modulations may also be inherited by sequence voices and overridden per step. These two slots coexist with the independent `lfoFreq`/`lfoDepth` pitch vibrato; they are not accepted by timed tones, chords .
|
|
221
397
|
|
|
222
398
|
## `playChord(options)`
|
|
223
399
|
|
|
224
|
-
Starts 1 to 32 pitches as one atomic event. Exactly one pitch array is
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
|
228
|
-
|
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
| `
|
|
241
|
-
| `
|
|
242
|
-
| `
|
|
243
|
-
| `
|
|
244
|
-
| `
|
|
245
|
-
| `
|
|
246
|
-
| `
|
|
247
|
-
| `
|
|
248
|
-
| `
|
|
249
|
-
| `
|
|
250
|
-
| `
|
|
251
|
-
| `
|
|
252
|
-
| `
|
|
253
|
-
|
|
254
|
-
`
|
|
255
|
-
|
|
256
|
-
`detune` fields.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
261
|
-
objects. A string or number step is rejected with `invalid_step_type`.
|
|
262
|
-
|
|
263
|
-
Pattern-level keys:
|
|
264
|
-
|
|
265
|
-
| Key | Type | Default | Description |
|
|
266
|
-
| --- | --- | ---: | --- |
|
|
267
|
-
| `steps` | object[] | required | Between 1 and 512 steps. |
|
|
268
|
-
| `bpm` | number | `120` | Tempo used by note values. |
|
|
269
|
-
| `swing` | number | `0` | Alternating timing offset. |
|
|
270
|
-
| `stepDuration` | number | `100` | Fallback step length in ms. |
|
|
271
|
-
| `noteValue` | string | none | Shared musical step length. |
|
|
272
|
-
| `attack` | integer | `50` | Inherited attack. |
|
|
273
|
-
| `release` | integer | `50` | Inherited release. |
|
|
274
|
-
| `volume` | number | `1` | Inherited event level. |
|
|
275
|
-
| `waveType` | integer | `SINE` | Inherited waveform. |
|
|
276
|
-
| `pan` | number | `0` | Inherited starting pan. |
|
|
277
|
-
| `panEnd` | number | same as root `pan` | Inherited ending pan. |
|
|
278
|
-
| `frequencyEnd` | number | `0` | Inherited sweep target. |
|
|
279
|
-
| `lfoFreq` | number | `0` | Inherited vibrato rate. |
|
|
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. |
|
|
300
|
-
|
|
301
|
-
Step keys:
|
|
302
|
-
|
|
303
|
-
| Key | Type | Description |
|
|
304
|
-
| --- | --- | --- |
|
|
305
|
-
| `note` | string | Pitch name; exclusive with `frequency` and `rest`. |
|
|
306
|
-
| `frequency` | number | Pitch in Hz; exclusive with `note` and `rest`. |
|
|
307
|
-
| `rest` | Boolean | Must be exactly `true` for a rest. |
|
|
308
|
-
| `duration` | integer | Explicit step length in ms. |
|
|
309
|
-
| `noteValue` | string | Step length derived from `bpm`. |
|
|
310
|
-
| `attack`, `release`, `volume`, `waveType` | number | Override the pattern value for this step. |
|
|
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. |
|
|
321
|
-
|
|
322
|
-
Step timing precedence is `duration`, step `noteValue`, pattern `noteValue`,
|
|
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.
|
|
400
|
+
Starts 1 to 32 pitches as one atomic event. Exactly one pitch array is required.
|
|
401
|
+
|
|
402
|
+
| Key | Type | Default | Description |
|
|
403
|
+
| ------------- | -------- | -------------------: | -------------------------------------------------------------- |
|
|
404
|
+
| `frequencies` | number[] | required alternative | Frequencies in Hz. |
|
|
405
|
+
| `notes` | string[] | required alternative | Note names. |
|
|
406
|
+
| `duration` | integer | `500` | Total chord length in ms. |
|
|
407
|
+
| `attack` | integer | `50` | Fade-in time in ms. |
|
|
408
|
+
| `release` | integer | `50` | Fade-out time in ms. |
|
|
409
|
+
| `volume` | number | `1` | Chord event level. |
|
|
410
|
+
| `waveType` | integer | `SINE` | Waveform for every voice. |
|
|
411
|
+
| `panSpread` | number | `0` | Even stereo spread from 0 to 1. |
|
|
412
|
+
| `pans` | number[] | none | Per-note pan positions; overrides spread for supplied entries. |
|
|
413
|
+
| `noiseRate` | number | `0` | Fixed update rate for noise voices. |
|
|
414
|
+
| `lowPassHz` | number | `0` | Fixed low-pass cutoff for every voice. |
|
|
415
|
+
| `pulseWidth` | number | `0.5` | Fixed square-wave duty cycle. |
|
|
416
|
+
| `waveTable` | number[] | none | Required when `waveType` is `WAVETABLE`. |
|
|
417
|
+
| `bitDepth` | integer | `0` | Fixed amplitude quantization; 0 disables it. |
|
|
418
|
+
| `decay` | integer | `0` | Fixed decay time shared by every voice. |
|
|
419
|
+
| `sustain` | number | `1` | Fixed sustain level shared by every voice. |
|
|
420
|
+
| `filterType` | string | `none` | Fixed filter type for every voice. |
|
|
421
|
+
| `filterHz` | number | `1000` | Fixed cutoff or center in Hz. |
|
|
422
|
+
| `filterQ` | number | `0.7071` | Fixed resonance. |
|
|
423
|
+
| `fmRatio` | number | `1` | Fixed modulator ratio. |
|
|
424
|
+
| `fmDepth` | number | `0` | Fixed modulation index; sine voices only. |
|
|
425
|
+
| `detunes` | number[] | none | Per-note offsets in cents, matched to the pitch array. |
|
|
426
|
+
| `spatialX` | number | omitted | Shared fixed left/right binaural direction. |
|
|
427
|
+
| `spatialY` | number | omitted | Shared fixed below/above binaural direction. |
|
|
428
|
+
| `bus` | string | `effects` | Category: music, effects, ui or ambience. |
|
|
429
|
+
| `effectSend` | number | omitted | Explicit processed share 0-1; replaces category selection. |
|
|
430
|
+
| `priority` | integer | `0` | Admission priority 0-3; higher voices are protected. |
|
|
431
|
+
|
|
432
|
+
`pans` and `detunes` cannot contain more entries than the pitch array, and 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 `detune` fields.
|
|
433
|
+
|
|
434
|
+
|
|
332
435
|
|
|
333
436
|
## Expressive controls
|
|
334
437
|
|
|
335
|
-
These options are
|
|
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.
|
|
438
|
+
These options are optional; omitting them leaves the note unchanged. They apply to `playTone`, to `playChord` as fixed values, and to sequence `voice`/step `overrides`.
|
|
338
439
|
|
|
339
440
|
### Decay and sustain
|
|
340
441
|
|
|
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.
|
|
442
|
+
After the attack, the level falls from full scale to `sustain` over `decay` milliseconds, then holds there until the release begins. With `decay: 0` and `sustain: 1` the envelope is the plain attack, full level and release.
|
|
344
443
|
|
|
345
444
|
```text
|
|
346
445
|
level
|
|
@@ -353,127 +452,85 @@ level
|
|
|
353
452
|
attack decay hold release
|
|
354
453
|
```
|
|
355
454
|
|
|
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.
|
|
455
|
+
The release scales whatever level the note has reached, so a release that begins during the decay stays continuous. `duration` still covers the whole event.
|
|
359
456
|
|
|
360
457
|
### Resonant filter
|
|
361
458
|
|
|
362
|
-
`filterType` inserts a 12 dB per octave state-variable filter after the
|
|
363
|
-
one-pole `lowPassHz` filter:
|
|
459
|
+
`filterType` inserts a 12 dB per octave state-variable filter after the one-pole `lowPassHz` filter:
|
|
364
460
|
|
|
365
|
-
| `filterType` | Passes
|
|
366
|
-
|
|
|
367
|
-
| `none`
|
|
368
|
-
| `lowpass`
|
|
369
|
-
| `highpass`
|
|
370
|
-
| `bandpass`
|
|
461
|
+
| `filterType` | Passes | `filterHz` means |
|
|
462
|
+
| ------------ | ----------------------------- | ---------------- |
|
|
463
|
+
| `none` | Everything; the default. | Not allowed. |
|
|
464
|
+
| `lowpass` | Frequencies below the cutoff. | Cutoff. |
|
|
465
|
+
| `highpass` | Frequencies above the cutoff. | Cutoff. |
|
|
466
|
+
| `bandpass` | A band around the center. | Center. |
|
|
371
467
|
|
|
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.
|
|
468
|
+
`filterQ` sets the resonance. The default 0.7071 is flat. Higher values add a peak at the cutoff: `lowpass` and `highpass` boost it by about `filterQ`, so Q 10 is roughly 20 dB. `bandpass` keeps unity gain at its center for every Q and only gets narrower.
|
|
376
469
|
|
|
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`.
|
|
470
|
+
`filterHzEnd` moves the cutoff over the whole event. The sweep is exponential: equal musical distance per unit of time, so 200 Hz to 3,200 Hz crosses 800 Hz at the midpoint. `filterHz` and `filterHzEnd` are held between 20 Hz and `sampleRate * 0.45`.
|
|
381
471
|
|
|
382
472
|
### Frequency modulation
|
|
383
473
|
|
|
384
|
-
A positive `fmDepth`, or any `fmDepthEnd`, turns a `SINE` voice into a
|
|
385
|
-
two-operator phase-modulated voice:
|
|
474
|
+
A positive `fmDepth`, or any `fmDepthEnd`, turns a `SINE` voice into a two-operator phase-modulated voice:
|
|
386
475
|
|
|
387
476
|
```text
|
|
388
477
|
output = sin(2 * pi * phase + fmDepth * sin(2 * pi * modulatorPhase))
|
|
389
478
|
```
|
|
390
479
|
|
|
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.
|
|
480
|
+
`fmRatio` sets the modulator frequency as a multiple of the carrier's current frequency, so pitch sweeps and vibrato keep the ratio. `fmDepth` is the modulation index in radians. Index 0 is a plain sine; each increase adds sidebands at the carrier frequency plus and minus multiples of the modulator frequency. `fmDepthEnd` moves the index linearly over the event. Unlike the other `*End` keys, 0 is a real target, so omit the key for no sweep.
|
|
397
481
|
|
|
398
482
|
FM works only on `SINE`. Another waveform is rejected with `fm_requires_sine`.
|
|
399
483
|
|
|
400
484
|
### Detune
|
|
401
485
|
|
|
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`.
|
|
486
|
+
`detune` shifts the pitch by cents: the frequency is multiplied by `2 ^ (cents / 1200)`, so 100 cents is one semitone. It applies to `note`, `frequency` and `frequencyEnd`. In a chord, `detunes` supplies one offset per pitch instead. The shifted pitch must stay between 1 Hz and the frequency ceiling, otherwise the call is rejected with `out_of_range` on `detune` or `detunes`.
|
|
408
487
|
|
|
409
488
|
### Rules across keys
|
|
410
489
|
|
|
411
|
-
The rules below
|
|
412
|
-
step, that means the step over the pattern root. A pattern made only of rests is
|
|
413
|
-
not checked.
|
|
490
|
+
The rules below apply to final sounding values. Sequence overrides inherit from root voice; ADSR stays in milliseconds without a note-length budget.
|
|
414
491
|
|
|
415
|
-
| Reason
|
|
416
|
-
|
|
|
417
|
-
| `sustain_without_decay`
|
|
418
|
-
| `decay_exceeds_remaining_duration` | `attack` plus `decay` is longer than the event.
|
|
419
|
-
| `filter_type_required`
|
|
420
|
-
| `fm_requires_sine`
|
|
421
|
-
| `fm_depth_required`
|
|
492
|
+
| Reason | Rejected when |
|
|
493
|
+
| ---------------------------------- | ------------------------------------------------------------------------------ |
|
|
494
|
+
| `sustain_without_decay` | `sustain` is below 1 and `decay` is 0. |
|
|
495
|
+
| `decay_exceeds_remaining_duration` | `attack` plus `decay` is longer than the event. |
|
|
496
|
+
| `filter_type_required` | `filterHz`, `filterHzEnd` or `filterQ` is set and `filterType` is `none`. |
|
|
497
|
+
| `fm_requires_sine` | `fmDepth` is above 0, or `fmDepthEnd` is set, on a waveform other than `SINE`. |
|
|
498
|
+
| `fm_depth_required` | `fmRatio` is set while FM is off. |
|
|
422
499
|
|
|
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.
|
|
500
|
+
For the decay rule, `attack` is the explicit value or the 50 ms default, held below `duration`. Set a short `attack` explicitly when the decay is long.
|
|
425
501
|
|
|
426
502
|
## `setVolume(options)`
|
|
427
503
|
|
|
428
504
|
Changes the master level with a short smoothing ramp.
|
|
429
505
|
|
|
430
|
-
| Key
|
|
431
|
-
|
|
|
432
|
-
| `volume` | number |
|
|
506
|
+
| Key | Type | Default | Range |
|
|
507
|
+
| -------- | ------ | ------: | -----: |
|
|
508
|
+
| `volume` | number | `1` | 0 to 2 |
|
|
433
509
|
|
|
434
510
|
## `stopAll()`
|
|
435
511
|
|
|
436
|
-
Fades active voices over roughly 20 ms to avoid clicks.
|
|
437
|
-
future pattern steps.
|
|
512
|
+
Fades active voices over roughly 20 ms to avoid clicks, stops future native sequence events and invalidates effect histories as panic. Panic fade tails route dry immediately, so they cannot refill the invalidated histories. Processing configuration remains available for future attacks. Disabling an effect instead drains its existing tail.
|
|
438
513
|
|
|
439
514
|
## `shutdown()`
|
|
440
515
|
|
|
441
|
-
Cancels
|
|
442
|
-
resources. Call it when the owner closes.
|
|
516
|
+
Cancels all sequence scheduling, stops the stream and releases voice/effect resources outside the audio callback. Call it when the owner closes, after cancelling app-owned timers/retries. It emits no completion event.
|
|
443
517
|
|
|
444
|
-
## `
|
|
518
|
+
## `sequenceend` event
|
|
445
519
|
|
|
446
|
-
|
|
520
|
+
Each accepted sequence emits `{ sequenceId, reason: 'completed' | 'stopped' }` when its traversal completes or an explicit stop applies. Delivery happens asynchronously outside the audio callback; queued events retain their identifier when internal slots are reused. Completion marks the end of scheduling: releases and effect tails may remain audible. Filter by the identifier before chaining UI actions; concatenate steps in one sequence when the next note must start sample accurately.
|
|
447
521
|
|
|
448
522
|
```javascript
|
|
449
|
-
function
|
|
450
|
-
|
|
523
|
+
function onSequenceEnd (event) {
|
|
524
|
+
if (event.sequenceId === musicId && event.reason === 'completed') showScore()
|
|
451
525
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
synth.removeEventListener('patternend', onPatternEnd)
|
|
526
|
+
synth.addEventListener('sequenceend', onSequenceEnd)
|
|
527
|
+
// During owner cleanup:
|
|
528
|
+
synth.removeEventListener('sequenceend', onSequenceEnd)
|
|
456
529
|
```
|
|
457
530
|
|
|
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
|
-
|
|
473
531
|
## Strict validation
|
|
474
532
|
|
|
475
|
-
|
|
476
|
-
native log entry:
|
|
533
|
+
Rejected Boolean calls return `false`; `playSequence` returns `0`. Where structured validation diagnostics are emitted, they follow this format:
|
|
477
534
|
|
|
478
535
|
```text
|
|
479
536
|
event=validation_warning method=playTone key=duraton reason=unsupported_option action=rejected value=200
|
|
@@ -481,41 +538,42 @@ event=validation_warning method=playTone key=duraton reason=unsupported_option a
|
|
|
481
538
|
|
|
482
539
|
Common reasons include:
|
|
483
540
|
|
|
484
|
-
| Reason
|
|
485
|
-
|
|
|
486
|
-
| `unsupported_option`
|
|
487
|
-
| `
|
|
488
|
-
| `non_numeric_or_non_finite`
|
|
489
|
-
| `not_integer`
|
|
490
|
-
| `out_of_range`
|
|
491
|
-
| `missing_required`
|
|
492
|
-
| `not_string`, `not_array`, `not_boolean` | A field has the wrong container or primitive type.
|
|
493
|
-
| `invalid_note`
|
|
494
|
-
| `unsupported_note_value`
|
|
495
|
-
| `unsupported_value`
|
|
496
|
-
| `invalid_sample_count`
|
|
497
|
-
| `missing_pitch_or_rest`
|
|
498
|
-
| `conflicting_pitch_fields`
|
|
499
|
-
| `conflicting_spatial_fields`
|
|
500
|
-
| `invalid_voice_count`
|
|
501
|
-
| `invalid_step_count`
|
|
502
|
-
| `too_many_values`
|
|
503
|
-
| `attack_greater_or_equal_duration`
|
|
504
|
-
| `release_exceeds_remaining_duration`
|
|
505
|
-
| `decay_exceeds_remaining_duration`
|
|
506
|
-
| `sustain_without_decay`
|
|
507
|
-
| `filter_type_required`
|
|
508
|
-
| `fm_requires_sine`
|
|
509
|
-
| `fm_depth_required`
|
|
510
|
-
|
|
511
|
-
Explicit values are not clamped. Read `getDefaults().limits` when accepting
|
|
512
|
-
|
|
541
|
+
| Reason | Meaning |
|
|
542
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
543
|
+
| `unsupported_option` | The option name is not part of that method or step. |
|
|
544
|
+
| `not_object` | A sequence step or options dictionary is not an object. |
|
|
545
|
+
| `non_numeric_or_non_finite` | A numeric field is the wrong type, `NaN` or infinite. |
|
|
546
|
+
| `not_integer` | An integer field contains a fractional number. |
|
|
547
|
+
| `out_of_range` | A number falls outside the documented range. |
|
|
548
|
+
| `missing_required` | A required pitch array or `WAVETABLE` sample array is missing. |
|
|
549
|
+
| `not_string`, `not_array`, `not_boolean` | A field has the wrong container or primitive type. |
|
|
550
|
+
| `invalid_note` | A note name cannot be parsed. |
|
|
551
|
+
| `unsupported_note_value` | A note value is not one of the ten supported values. |
|
|
552
|
+
| `unsupported_value` | `mixingProfile`, `audioSession` or `filterType` is not one of the listed strings. |
|
|
553
|
+
| `invalid_sample_count` | `waveTable` has fewer than 2 or more than 64 samples. |
|
|
554
|
+
| `missing_pitch_or_rest` | A sounding event has no pitch and is not a rest. |
|
|
555
|
+
| `conflicting_pitch_fields` | A call or step provides more than one pitch source. |
|
|
556
|
+
| `conflicting_spatial_fields` | Spatial coordinates are combined with stereo pan fields, including inherited sequence voice values. |
|
|
557
|
+
| `invalid_voice_count` | A chord has fewer than 1 or more than 32 pitches. |
|
|
558
|
+
| `invalid_step_count` | The step array has fewer than 1 or more than 512 entries. |
|
|
559
|
+
| `too_many_values` | `pans` or `detunes` is longer than the chord. |
|
|
560
|
+
| `attack_greater_or_equal_duration` | An explicit attack leaves no time for the event. |
|
|
561
|
+
| `release_exceeds_remaining_duration` | An explicit release does not fit after attack. |
|
|
562
|
+
| `decay_exceeds_remaining_duration` | `attack` plus `decay` does not fit in the event. |
|
|
563
|
+
| `sustain_without_decay` | `sustain` is below 1 while `decay` is 0. |
|
|
564
|
+
| `filter_type_required` | A filter key is set while `filterType` is `none`. |
|
|
565
|
+
| `fm_requires_sine` | FM is active on a waveform other than `SINE`. |
|
|
566
|
+
| `fm_depth_required` | `fmRatio` is set while FM is off. |
|
|
567
|
+
|
|
568
|
+
Explicit values are not clamped. Read `getDefaults().limits` when accepting user input and decide in the app whether to reject or constrain it.
|
|
569
|
+
|
|
570
|
+
Some transport and control rejection paths do not emit a structured warning. Check acceptance directly; absence of a log entry does not prove success.
|
|
513
571
|
|
|
514
572
|
## Binaural front-plane positioning
|
|
515
573
|
|
|
516
|
-
`playTone`, `playChord
|
|
574
|
+
`playTone`, `playChord`, `noteOn` and sequence voice/overrides accept `spatialX` and `spatialY`, finite numbers from -1 to 1. Supplying either enables headphone binaural processing for that event; the omitted coordinate is 0. Timed events keep a fixed position; spatial held voices support smooth updates. `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
575
|
|
|
518
|
-
Spatial coordinates cannot be combined with `pan`, `panEnd`, `pans` or `panSpread`, including conflicting fields inherited by a
|
|
576
|
+
Spatial coordinates cannot be combined with `pan`, `panEnd`, `pans` or `panSpread`, including conflicting fields inherited by a sequence 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
577
|
|
|
520
578
|
```javascript
|
|
521
579
|
var accepted = synth.playTone({
|
|
@@ -532,64 +590,54 @@ Listen with stereo headphones. Height perception depends on the listener, headph
|
|
|
532
590
|
|
|
533
591
|
## `getStats()`
|
|
534
592
|
|
|
535
|
-
|
|
593
|
+
Use `typeof synth.getStats === 'function'` to check that the expected module is installed before enabling a diagnostics panel.
|
|
594
|
+
|
|
595
|
+
Returns a new snapshot without resetting counters. Available before start and after shutdown. Occupancy is the last completed render block; queued commands are read at query time. No audio-thread allocation, locks or waiting are added.
|
|
596
|
+
|
|
597
|
+
| Field | Meaning |
|
|
598
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
599
|
+
| `activeVoices` | Occupied primary slots, including binaural FIR draining. |
|
|
600
|
+
| `maxVoices` | Configured primary-slot capacity (default 8 before start). |
|
|
601
|
+
| `peakActiveVoices` | Highest primary occupancy since reset/start, including peaks between queries. |
|
|
602
|
+
| `tailVoices` | External fade tails from replacement or `stopAll`, excluding primary FIR drain. |
|
|
603
|
+
| `spatialVoices` | Spatial primary slots, including FIR draining; excludes external tails. |
|
|
604
|
+
| `voiceStealCount` | Primary replacements while all slots are occupied. |
|
|
605
|
+
| `rejectedRequestCount` | Counted API rejections, including sequence-publication rejection; not a complete rejected-call tally on every native path. |
|
|
606
|
+
| `rejectedRequestsByReason` | `{ validation, engineStopped, queueFull }`; sum equals the recorded rejection total. |
|
|
607
|
+
| `priorityDropCount` | Events/chords dropped because eligible voices could not accommodate them. |
|
|
608
|
+
| `rejectedPublicationCount` | Rejected sequence publications due to capacity or bank pressure. |
|
|
609
|
+
| `pendingCommands` | Published entries awaiting the renderer; chord notes count separately, future scheduler steps are excluded. |
|
|
610
|
+
| `sampleRate` | Current native rate, or the last rate after shutdown; 44100 before start. |
|
|
611
|
+
| `callbackFrames` | Frames processed in the most recent callback; 0 before start/after shutdown. |
|
|
612
|
+
| `renderLoad` | Last callback elapsed seconds / (`callbackFrames / sampleRate`); may exceed 1. |
|
|
613
|
+
| `peakRenderLoad` | Maximum callback load since diagnostics were enabled or statistics reset; accumulates across signal windows. |
|
|
614
|
+
| `outputPeakDbFS` | `{ left, right }` peaks of final float output in the latest completed approximately 200 ms window. |
|
|
615
|
+
| `outputRmsDbFS` | `{ left, right }` RMS of that same window. |
|
|
616
|
+
| `preLimiterPeakDb` | Maximum native peak before the limiter since reset, in dBFS; diagnostics required. |
|
|
617
|
+
| `maxLimiterReductionDb` | Maximum native limiter attenuation since reset, in dB; diagnostics required. A brief intervention persists across UI polls. |
|
|
618
|
+
| `limiterReductionDb` | Maximum limiter attenuation in that window, as a positive dB magnitude. |
|
|
619
|
+
| `underrunCount` | Android Oboe accumulated backend xruns since reset/start when supported; otherwise `null`. |
|
|
620
|
+
| `bufferFrames` | Android Oboe output buffer size; otherwise `null`. This is distinct from `callbackFrames`. |
|
|
621
|
+
| `estimatedOutputLatencyMs` | Android timestamp-based stream-to-DAC estimate plus module lookahead, when valid; otherwise `null` (always `null` on this iOS backend). |
|
|
536
622
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
623
|
+
At this source pin, iOS early rejections in tempo/sequence operations and a
|
|
624
|
+
stopped `setMasterProcessing` call can bypass rejection counters. Incompatible
|
|
625
|
+
held spatial-mode or modulation updates also have uncounted iOS returns.
|
|
626
|
+
Android counts more of these cases, but an unknown/retired `stopSequence` return
|
|
627
|
+
can also be uncounted. The reason sum equals the recorded total, not every
|
|
628
|
+
rejected request. These counters are diagnostics; the API return is authoritative.
|
|
540
629
|
|
|
541
|
-
|
|
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). |
|
|
630
|
+
`renderLoad`, `peakRenderLoad`, `outputPeakDbFS`, `outputRmsDbFS`, `preLimiterPeakDb`, `maxLimiterReductionDb` and `limiterReductionDb` are `null` until enabled and measured, and after shutdown. Android backend metrics (`underrunCount`, `bufferFrames` and `estimatedOutputLatencyMs`) are best-effort readings independent of this diagnostic switch. Signal windows use the final output after master volume, limiter and clamping, with a silence floor of -120 dBFS. A query does not start or consume a window. iOS does not expose a trustworthy underrun count or hardware output buffer size through this source-node backend, so those fields are `null`. 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. 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. A zero underrun count does not prove clean sound. Render load includes elapsed callback work, not total app CPU.
|
|
563
631
|
|
|
564
|
-
|
|
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.
|
|
632
|
+
Render load is a fraction: multiply by 100 for a percentage. For example, `0.012` is 1.2% of the block budget, while `1.0` is 100%. An open stream still renders silent blocks, including output/limiter work and enabled signal measurements, so no active voices does not imply zero load. Callback elapsed time varies with scheduling and system activity; displayed decimal places do not establish measurement accuracy. Compare the same configuration on the same device. Percentages from a simulator and a phone do not provide a direct performance comparison.
|
|
576
633
|
|
|
577
634
|
## `setStatsEnabled(enabled)`
|
|
578
635
|
|
|
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.
|
|
636
|
+
Accepts a Boolean directly, returns Boolean acceptance. Default `false`. Enabling starts a new diagnostic window, without restarting audio. Calling it again with `true` also starts a fresh window. Disabling hides timing and signal fields immediately. Basic counters remain available in either mode. The setting persists across shutdown/start, while a new engine starts with fresh counters.
|
|
584
637
|
|
|
585
638
|
## `resetStats()`
|
|
586
639
|
|
|
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.
|
|
640
|
+
Returns nothing. Resets accumulated counters and maxima without stopping sounds, clearing queued commands or enabling diagnostics. Primary occupancy becomes the initial maximum. Render-owned changes take effect at the next callback boundary; queries immediately hide stale measurements and report reset accumulated values. A new successful start after shutdown also resets statistics. Shutdown clears occupancy and commands while retaining accumulated basic totals and maxima.
|
|
593
641
|
|
|
594
642
|
```javascript
|
|
595
643
|
// Optional diagnostics; poll at 5 Hz and stop the timer with the owner.
|
|
@@ -600,3 +648,9 @@ if (synth.setStatsEnabled(true)) {
|
|
|
600
648
|
// Later: clearInterval(statsTimer); synth.setStatsEnabled(false)
|
|
601
649
|
}
|
|
602
650
|
```
|
|
651
|
+
|
|
652
|
+
## Routing and voice priority
|
|
653
|
+
|
|
654
|
+
`playTone`, `playChord`, `noteOn` and sequence voices accept `bus`, `effectSend` and `priority`. Categories are `music`, `effects` (default), `ui` and `ambience`. An omitted send follows `setMasterProcessing({ applyTo })`; an explicit 0-1 send replaces that selection. Priority is integer 0-3, default 0. When the pool fills, the lowest eligible priority is replaced first; a lower-priority event cannot steal a higher-priority voice. Existing release/age tie-breakers remain. Chords are admitted or dropped completely; no voices are reserved. `getStats().priorityDropCount` counts these drops; `rejectedPublicationCount` counts unavailable sequence capacity/banks separately from output diagnostics.
|
|
655
|
+
|
|
656
|
+
`updateVoice` additionally accepts `noiseRate`, `lowPassHz`, `pan`, `spatialX`, `spatialY` and `effectSend`, with 10 ms transitions. Noise rate 0 means white noise; low-pass 0 means bypass. Noise phase, oscillator phase and spatial history survive compatible edits. Pan updates require a stereo voice; coordinates require a voice started with spatial coordinates. An omitted coordinate retains its previous value. The source/filter type still requires a new attack. Updates are generation-scoped so queued old commands cannot change a later attack sharing the public voice ID.
|