@maccesar/titools 5.2.3 → 5.2.5
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 +96 -87
- package/docs/examples.md +10 -3
- package/package.json +2 -2
- package/skills/ti-synthengine/SKILL.md +50 -18
- package/skills/ti-synthengine/assets/example/README.md +19 -6
- package/skills/ti-synthengine/assets/example/app.js +69 -12
- package/skills/ti-synthengine/assets/index.d.ts +106 -0
- package/skills/ti-synthengine/references/advanced-examples.md +34 -9
- package/skills/ti-synthengine/references/api.md +327 -246
- package/skills/ti-synthengine/references/audio-engine.md +28 -14
- package/skills/ti-synthengine/references/audio-pipeline-validation.md +1 -1
- package/skills/ti-synthengine/references/examples.md +5 -5
- package/skills/ti-synthengine/references/guide.md +40 -21
- package/skills/ti-synthengine/references/instruments-and-effects.md +164 -0
- package/skills/ti-synthengine/references/native-tone-repetition-validation.md +59 -0
- package/skills/ti-synthengine/references/native-variation-proposal.md +182 -0
- package/skills/ti-synthengine/references/native-variation-validation.md +63 -0
- package/skills/ti-synthengine/references/native-variation.md +285 -0
- package/skills/ti-synthengine/references/recipes-interaction.md +3 -1
- package/skills/ti-synthengine/references/recipes.md +3 -2
- package/skills/ti-synthengine/references/recommendations.md +5 -5
- package/skills/ti-synthengine/references/sound-design.md +18 -16
- package/skills/ti-synthengine/references/source-notes.md +54 -37
- package/skills/ti-synthengine/references/source-snapshot.json +188 -144
- package/skills/ti-synthengine/references/stats-performance.md +1 -1
- package/skills/ti-synthengine/references/troubleshooting.md +43 -35
|
@@ -7,14 +7,15 @@ var sequenceOwner = require('lib/sequenceOwner')(Synth, {
|
|
|
7
7
|
function publishSequence(options) { return sequenceOwner.submit(options) }
|
|
8
8
|
|
|
9
9
|
var defaults = Synth.getDefaults()
|
|
10
|
-
if (!defaults || defaults.apiVersion !== '1.
|
|
11
|
-
throw new Error('TiSynthEngine 1.
|
|
10
|
+
if (!defaults || defaults.apiVersion !== '1.1.0') {
|
|
11
|
+
throw new Error('TiSynthEngine 1.1.0 is required')
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
if (!defaults.limits || !defaults.limits.spatialX || !defaults.limits.spatialY
|
|
15
15
|
|| typeof Synth.getStats !== 'function' || typeof Synth.resetStats !== 'function'
|
|
16
|
-
|| typeof Synth.
|
|
17
|
-
|
|
16
|
+
|| typeof Synth.enableStats !== 'function' || typeof Synth.disableStats !== 'function'
|
|
17
|
+
|| typeof Synth.repeatTone !== 'function' || typeof Synth.updateTone !== 'function' || typeof Synth.stopTone !== 'function') {
|
|
18
|
+
throw new Error('Install the current TiSynthEngine 1.1.0 packages, then rebuild and reinstall this app')
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
var WAVE = defaults.waveTypes
|
|
@@ -23,6 +24,7 @@ var engineRunning = false
|
|
|
23
24
|
var startTimer = null
|
|
24
25
|
var statsTimer = null
|
|
25
26
|
var diagnosticsEnabled = false
|
|
27
|
+
var repeatingToneId = 0
|
|
26
28
|
var selectedSession = 'playback'
|
|
27
29
|
var masterVolume = 1
|
|
28
30
|
var binauralEnabled = true
|
|
@@ -200,7 +202,7 @@ function makeHero() {
|
|
|
200
202
|
height: Ti.UI.SIZE,
|
|
201
203
|
top: dp(16)
|
|
202
204
|
})
|
|
203
|
-
badgeRow.add(makeBadge('API 1.
|
|
205
|
+
badgeRow.add(makeBadge('API 1.1.0', COLORS.indigo))
|
|
204
206
|
badgeRow.add(makeBadge('ANDROID + iOS', COLORS.surfaceMuted))
|
|
205
207
|
hero.add(badgeRow)
|
|
206
208
|
hero.add(statusPill)
|
|
@@ -399,6 +401,40 @@ waveNames.forEach(function (name, index) {
|
|
|
399
401
|
waveSection.add(Ti.UI.createView({ height: dp(12) }))
|
|
400
402
|
scrollView.add(waveSection)
|
|
401
403
|
|
|
404
|
+
// A repeated finite recipe uses its native owner ID; there is no attack timer.
|
|
405
|
+
function stopRepeatingTone() {
|
|
406
|
+
if (!repeatingToneId) return true
|
|
407
|
+
if (!Synth.stopTone({ toneId: repeatingToneId })) return false
|
|
408
|
+
repeatingToneId = 0
|
|
409
|
+
return true
|
|
410
|
+
}
|
|
411
|
+
var repeatSection = makeSection('Finite-tone repetition',
|
|
412
|
+
'The same complete 105 ms shot repeats every 200 ms. Edit its future attacks; stop lets current shots finish.',
|
|
413
|
+
COLORS.teal)
|
|
414
|
+
repeatSection.add(makeAction({
|
|
415
|
+
title: 'START REPEATING', color: COLORS.teal, pressedColor: COLORS.tealPressed,
|
|
416
|
+
onPress: function () {
|
|
417
|
+
if (repeatingToneId) return true
|
|
418
|
+
var id = Synth.repeatTone({ frequency: 1380, frequencyEnd: 340,
|
|
419
|
+
duration: 105, attack: 2, release: 40, waveType: WAVE.SQUARE,
|
|
420
|
+
volume: 0.1, intervalMs: 200 })
|
|
421
|
+
if (!Number.isInteger(id) || id <= 0) return false
|
|
422
|
+
repeatingToneId = id
|
|
423
|
+
return true
|
|
424
|
+
}
|
|
425
|
+
}))
|
|
426
|
+
repeatSection.add(makeAction({
|
|
427
|
+
title: 'EDIT REPEATING', color: COLORS.teal, pressedColor: COLORS.tealPressed,
|
|
428
|
+
onPress: function () {
|
|
429
|
+
return !!repeatingToneId && Synth.updateTone({ toneId: repeatingToneId, intervalMs: 150, volume: 0.08 })
|
|
430
|
+
}
|
|
431
|
+
}))
|
|
432
|
+
repeatSection.add(makeAction({
|
|
433
|
+
title: 'STOP REPEATING', color: COLORS.surfaceMuted, pressedColor: COLORS.surfaceRaised,
|
|
434
|
+
onPress: stopRepeatingTone
|
|
435
|
+
}))
|
|
436
|
+
scrollView.add(repeatSection)
|
|
437
|
+
|
|
402
438
|
// ---------------------------------------------------------------------------
|
|
403
439
|
// 2. Note names
|
|
404
440
|
// ---------------------------------------------------------------------------
|
|
@@ -835,6 +871,26 @@ expressiveSection.add(makeAction({
|
|
|
835
871
|
}
|
|
836
872
|
}))
|
|
837
873
|
|
|
874
|
+
// Every press is a fresh native attack. Older packages receive the authored recipe.
|
|
875
|
+
expressiveSection.add(makeAction({
|
|
876
|
+
title: 'ATTACK VARIATION\nMetal strike · native ranges',
|
|
877
|
+
color: COLORS.teal,
|
|
878
|
+
pressedColor: COLORS.tealPressed,
|
|
879
|
+
status: 'Metal strike · a new native selection on each attack',
|
|
880
|
+
onPress: function () {
|
|
881
|
+
var options = { frequency: 620, duration: 180, attack: 1, decay: 65,
|
|
882
|
+
sustain: 0.12, release: 110, waveType: WAVE.SINE,
|
|
883
|
+
fmRatio: 2.7, fmDepth: 3.2, fmDepthEnd: 0.4, volume: 0.3 }
|
|
884
|
+
if (defaults.capabilities && defaults.capabilities.variation === true
|
|
885
|
+
&& defaults.variationSchemaVersion === 1) {
|
|
886
|
+
options.variation = { amount: 1, ranges: {
|
|
887
|
+
volume: [0.291, 0.3], fmDepth: [2.944, 3.456], pitchCents: [-12, 12]
|
|
888
|
+
} }
|
|
889
|
+
}
|
|
890
|
+
return Synth.playTone(options)
|
|
891
|
+
}
|
|
892
|
+
}))
|
|
893
|
+
|
|
838
894
|
var patternEndLabel = makeText('sequenceend: no pattern yet', {
|
|
839
895
|
top: dp(8),
|
|
840
896
|
left: dp(16),
|
|
@@ -979,7 +1035,7 @@ var sessionLabel = makeText('Session: playback', { left: dp(16), right: dp(16),
|
|
|
979
1035
|
color: COLORS.blueDark, pressedColor: COLORS.blue,
|
|
980
1036
|
accessibilityHint: 'Restarts the engine with this session policy',
|
|
981
1037
|
onPress: function () {
|
|
982
|
-
sequenceOwner.reset(); Synth.shutdown()
|
|
1038
|
+
repeatingToneId = 0; sequenceOwner.reset(); Synth.shutdown()
|
|
983
1039
|
engineRunning = false
|
|
984
1040
|
selectedSession = session
|
|
985
1041
|
patternEndLabel.text = 'sequenceend: cancelled by shutdown (no event)'
|
|
@@ -1026,7 +1082,7 @@ statsSection.add(makeAction({
|
|
|
1026
1082
|
title: 'TOGGLE DIAGNOSTICS', color: COLORS.orange, pressedColor: COLORS.orangePressed,
|
|
1027
1083
|
accessibilityHint: 'Enables or disables timing and signal measurements',
|
|
1028
1084
|
onPress: function () {
|
|
1029
|
-
if (!Synth.
|
|
1085
|
+
if (!(diagnosticsEnabled ? Synth.disableStats() : Synth.enableStats())) return false
|
|
1030
1086
|
diagnosticsEnabled = !diagnosticsEnabled
|
|
1031
1087
|
stopStatsPolling()
|
|
1032
1088
|
if (diagnosticsEnabled) statsTimer = setInterval(refreshStats, 200)
|
|
@@ -1083,10 +1139,10 @@ boostRow.add(makeGridAction({
|
|
|
1083
1139
|
title: 'STOP ALL\nRelease active voices',
|
|
1084
1140
|
color: COLORS.redDark,
|
|
1085
1141
|
pressedColor: COLORS.red,
|
|
1086
|
-
status: '
|
|
1087
|
-
accessibilityHint: '
|
|
1142
|
+
status: 'Future attacks cancelled; active voices fading',
|
|
1143
|
+
accessibilityHint: 'Cancels future attacks and fades current voices',
|
|
1088
1144
|
onPress: function () {
|
|
1089
|
-
sequenceOwner.stop(); Synth.stopAll()
|
|
1145
|
+
repeatingToneId = 0; sequenceOwner.stop(); Synth.stopAll()
|
|
1090
1146
|
}
|
|
1091
1147
|
}))
|
|
1092
1148
|
controlSection.add(boostRow)
|
|
@@ -1150,8 +1206,9 @@ function hideAndroidActionBar() {
|
|
|
1150
1206
|
}
|
|
1151
1207
|
|
|
1152
1208
|
function cleanup() {
|
|
1209
|
+
if (!stopRepeatingTone()) Ti.API.warn('Repeating tone stop rejected; shutting down owner')
|
|
1153
1210
|
stopStatsPolling()
|
|
1154
|
-
Synth.
|
|
1211
|
+
Synth.disableStats()
|
|
1155
1212
|
diagnosticsEnabled = false
|
|
1156
1213
|
Synth.removeEventListener('sequenceend', onSequenceEnd)
|
|
1157
1214
|
win.removeEventListener('postlayout', onFirstPostLayout)
|
|
@@ -1164,7 +1221,7 @@ function cleanup() {
|
|
|
1164
1221
|
startTimer = null
|
|
1165
1222
|
}
|
|
1166
1223
|
if (engineRunning) {
|
|
1167
|
-
sequenceOwner.reset(); Synth.shutdown()
|
|
1224
|
+
repeatingToneId = 0; sequenceOwner.reset(); Synth.shutdown()
|
|
1168
1225
|
engineRunning = false
|
|
1169
1226
|
}
|
|
1170
1227
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** TiSynthEngine local 1.1.0 candidate, public API 1.1.0. */
|
|
2
|
+
declare namespace synth {
|
|
3
|
+
type Bus = 'music' | 'effects' | 'ui' | 'ambience';
|
|
4
|
+
type FilterType = 'none' | 'lowpass' | 'highpass' | 'bandpass';
|
|
5
|
+
type Range = [number, number];
|
|
6
|
+
interface Variation {
|
|
7
|
+
amount?: number;
|
|
8
|
+
seed?: number;
|
|
9
|
+
ranges?: Partial<Record<'volume' | 'filterHz' | 'filterQ' | 'fmDepth' |
|
|
10
|
+
'noiseRate' | 'lowPassHz' | 'pulseWidth' | 'pitchCents', Range>>;
|
|
11
|
+
}
|
|
12
|
+
interface VoiceOptions {
|
|
13
|
+
attack?: number; decay?: number; sustain?: number; release?: number;
|
|
14
|
+
volume?: number; waveType?: number; pan?: number;
|
|
15
|
+
lfoFreq?: number; lfoDepth?: number; noiseRate?: number; lowPassHz?: number;
|
|
16
|
+
pulseWidth?: number; waveTable?: number[]; bitDepth?: number;
|
|
17
|
+
filterType?: FilterType; filterHz?: number; filterQ?: number;
|
|
18
|
+
fmRatio?: number; fmDepth?: number; spatialX?: number; spatialY?: number;
|
|
19
|
+
bus?: Bus; effectSend?: number; priority?: number; variation?: Variation;
|
|
20
|
+
}
|
|
21
|
+
type Pitch = { note?: string; frequency?: never } | { frequency?: number; note?: never };
|
|
22
|
+
interface FiniteOptions extends VoiceOptions {
|
|
23
|
+
duration?: number; panEnd?: number; frequencyEnd?: number;
|
|
24
|
+
noiseRateEnd?: number; pulseWidthEnd?: number; filterHzEnd?: number;
|
|
25
|
+
fmDepthEnd?: number; detune?: number;
|
|
26
|
+
}
|
|
27
|
+
type ToneOptions = FiniteOptions & Pitch;
|
|
28
|
+
type RepeatingToneOptions = ToneOptions & { intervalMs: number };
|
|
29
|
+
/** Partial changes merge with the latest accepted recipe; never retriggers immediately. */
|
|
30
|
+
type ToneUpdate = Partial<FiniteOptions> & Pitch & { toneId: number; intervalMs?: number };
|
|
31
|
+
interface Modulation {
|
|
32
|
+
target: 'filterHz' | 'pulseWidth' | 'fmDepth';
|
|
33
|
+
wave?: 'sine' | 'triangle'; rate?: number; sync?: string; depth?: number;
|
|
34
|
+
}
|
|
35
|
+
type NoteOptions = VoiceOptions & Pitch & { voiceId: number; detune?: number; modulations?: Modulation[] };
|
|
36
|
+
type NoteUpdate = Pick<VoiceOptions, 'volume' | 'pan' | 'lfoFreq' | 'lfoDepth' |
|
|
37
|
+
'noiseRate' | 'lowPassHz' | 'pulseWidth' | 'filterHz' | 'filterQ' |
|
|
38
|
+
'fmRatio' | 'fmDepth' | 'spatialX' | 'spatialY' | 'effectSend'> & Pitch &
|
|
39
|
+
{ voiceId: number; modulations?: Modulation[] };
|
|
40
|
+
type ChordOptions = VoiceOptions & { duration?: number; detune?: number;
|
|
41
|
+
detunes?: number[]; pans?: number[]; panSpread?: number } &
|
|
42
|
+
({ notes: string[]; frequencies?: never } | { frequencies: number[]; notes?: never });
|
|
43
|
+
interface StartOptions {
|
|
44
|
+
maxVoices?: number; maxSequences?: number;
|
|
45
|
+
mixingProfile?: 'conservative' | 'raw' | 'balanced' | 'speakerSafe';
|
|
46
|
+
audioSession?: 'playback' | 'ambient';
|
|
47
|
+
}
|
|
48
|
+
interface Defaults {
|
|
49
|
+
apiVersion: '1.1.0';
|
|
50
|
+
capabilities: { variation: true; toneRepetition: true; [key: string]: boolean };
|
|
51
|
+
variationSchemaVersion: 1;
|
|
52
|
+
limits: Record<string, { min: number; max: number }>;
|
|
53
|
+
defaults: Record<string, unknown>;
|
|
54
|
+
waveTypes: Record<'SINE' | 'SQUARE' | 'SAWTOOTH' | 'TRIANGLE' | 'NOISE' | 'WAVETABLE', number>;
|
|
55
|
+
mixingProfiles: string[]; audioSessions: string[]; buses: Bus[]; filterTypes: FilterType[];
|
|
56
|
+
}
|
|
57
|
+
interface SequenceVoice extends Omit<FiniteOptions, 'duration' | 'detune'> { modulations?: Modulation[] }
|
|
58
|
+
type SequenceStep = { duration?: number; length?: number; gate?: number;
|
|
59
|
+
overrides?: SequenceVoice; bus?: Bus; effectSend?: number; priority?: number } &
|
|
60
|
+
({ notes: number[]; frequencies?: never; rest?: never } |
|
|
61
|
+
{ frequencies: number[]; notes?: never; rest?: never } |
|
|
62
|
+
{ rest: true; notes?: never; frequencies?: never });
|
|
63
|
+
interface SequenceOptions {
|
|
64
|
+
timeUnit?: 'beats' | 'ms'; stepDuration?: number; gate?: number; loop?: boolean;
|
|
65
|
+
swing?: number; mode?: 'normal' | 'up' | 'down' | 'updown'; tuning?: number;
|
|
66
|
+
detune?: number; voice?: SequenceVoice; steps: SequenceStep[];
|
|
67
|
+
bus?: Bus; effectSend?: number; priority?: number;
|
|
68
|
+
}
|
|
69
|
+
interface SequenceState {
|
|
70
|
+
sequenceId: number; playing: boolean; step: number; beat: number;
|
|
71
|
+
position: number; bpm: number; revision: number;
|
|
72
|
+
}
|
|
73
|
+
function getDefaults(): Defaults;
|
|
74
|
+
function start(options?: StartOptions): boolean;
|
|
75
|
+
/** One finite attack. intervalMs is rejected; use repeatTone for repetition. */
|
|
76
|
+
function playTone(options: ToneOptions & { intervalMs?: never }): boolean;
|
|
77
|
+
/** Integer 1-60000 ms: positive module-owned ID, or 0 on rejection. */
|
|
78
|
+
function repeatTone(options: RepeatingToneOptions): number;
|
|
79
|
+
function updateTone(options: ToneUpdate): boolean;
|
|
80
|
+
/** Cancels future attacks at the next block; admitted attacks finish. */
|
|
81
|
+
function stopTone(options: { toneId: number }): boolean;
|
|
82
|
+
function playChord(options: ChordOptions): boolean;
|
|
83
|
+
function noteOn(options: NoteOptions): boolean;
|
|
84
|
+
function updateNote(options: NoteUpdate): boolean;
|
|
85
|
+
/** @deprecated Use updateNote; identical behavior and voiceId contract. */
|
|
86
|
+
function updateVoice(options: NoteUpdate): boolean;
|
|
87
|
+
function noteOff(options: { voiceId: number }): boolean;
|
|
88
|
+
function playSequence(options: SequenceOptions): number;
|
|
89
|
+
function updateSequence(options: Partial<Omit<SequenceOptions, 'timeUnit'>> & { sequenceId: number }): boolean;
|
|
90
|
+
function stopSequence(options: { sequenceId: number }): boolean;
|
|
91
|
+
function getSequenceState(options: { sequenceId: number }): SequenceState | null;
|
|
92
|
+
function setTempo(options: { bpm: number }): boolean;
|
|
93
|
+
function setVolume(options: { volume: number }): boolean;
|
|
94
|
+
function setMasterProcessing(options: Record<string, unknown>): boolean;
|
|
95
|
+
function enableStats(): boolean;
|
|
96
|
+
function disableStats(): boolean;
|
|
97
|
+
/** @deprecated Use enableStats or disableStats; identical diagnostic behavior. */
|
|
98
|
+
function setStatsEnabled(enabled: boolean): boolean;
|
|
99
|
+
function getStats(): Record<string, number | boolean | string | null>;
|
|
100
|
+
function resetStats(): void;
|
|
101
|
+
function stopAll(): void;
|
|
102
|
+
function shutdown(): void;
|
|
103
|
+
function addEventListener(name: 'sequenceend', listener: (event: { sequenceId: number; reason: 'completed' | 'stopped' }) => void): void;
|
|
104
|
+
function removeEventListener(name: 'sequenceend', listener: (event: { sequenceId: number; reason: 'completed' | 'stopped' }) => void): void;
|
|
105
|
+
}
|
|
106
|
+
export = synth;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `ec04f4e634075c41b55f66279eabcd78b9652d58` (2026-11-02). Published module 1.1.0; public API 1.1.0. Upstream `documentation/examples.md` SHA-256: `1b3be798d8f606381696d9e00cda330c11475402397e4760dcbc3041a39ef404`.
|
|
2
2
|
|
|
3
3
|
## Sustained game motor
|
|
4
4
|
|
|
5
5
|
Assume one running engine owned by this service. The positive voice ID belongs to the app; do not reuse it during the session or share its counter with an independent audio owner. There is no `duration` or `*End` target on `noteOn`.
|
|
6
6
|
|
|
7
7
|
```javascript
|
|
8
|
-
if (!['noteOn', 'noteOff', '
|
|
8
|
+
if (!['noteOn', 'noteOff', 'updateNote'].every(function (name) {
|
|
9
9
|
return typeof synth[name] === 'function'
|
|
10
10
|
})) throw new Error('Held-voice APIs are required')
|
|
11
11
|
|
|
@@ -25,7 +25,7 @@ function startMotor () {
|
|
|
25
25
|
|
|
26
26
|
function changeMotor (boosted) {
|
|
27
27
|
if (motorId === null) return false
|
|
28
|
-
return synth.
|
|
28
|
+
return synth.updateNote({ voiceId: motorId,
|
|
29
29
|
filterHz: boosted ? 320 : 220, volume: boosted ? 0.18 : 0.15 })
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -128,7 +128,7 @@ Call `setCreativeDrive(true)` only for a deliberate timbre change and check acce
|
|
|
128
128
|
This square voice modulates cutoff and PWM independently. The original `lfoFreq`/`lfoDepth` pitch vibrato remains a separate control. All LFOs run natively; edits preserve their phases and ramp contributions over 10 ms.
|
|
129
129
|
|
|
130
130
|
```javascript
|
|
131
|
-
if (typeof synth.noteOn !== 'function' || typeof synth.
|
|
131
|
+
if (typeof synth.noteOn !== 'function' || typeof synth.updateNote !== 'function') {
|
|
132
132
|
throw new Error('Held-voice APIs are required')
|
|
133
133
|
}
|
|
134
134
|
var modulatedId = 100001 // Reserve an unused ID from the owner's counter in an app.
|
|
@@ -141,23 +141,23 @@ if (!synth.noteOn({ voiceId: modulatedId, note: 'A3', waveType: WAVE.SQUARE,
|
|
|
141
141
|
] })) throw new Error('Modulated voice rejected')
|
|
142
142
|
|
|
143
143
|
function disableVoiceLfos () {
|
|
144
|
-
return synth.
|
|
144
|
+
return synth.updateNote({ voiceId: modulatedId, modulations: [] })
|
|
145
145
|
}
|
|
146
146
|
// Call disableVoiceLfos on the UI action and handle false.
|
|
147
147
|
// Later release this ID with the bounded release policy above, or shut down its owner.
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
Use `target: 'fmDepth'` only with `WAVE.SINE`; a filter target requires an active filter and PWM requires `WAVE.SQUARE`. Free rates are 0 to 20 Hz; synced subdivisions follow native transport BPM. Depth is normalized, with cutoff/PWM/FM contributions clamped to safe parameter ranges. `modulations: []` disables both slots; omitting the field on update preserves them. Copy the same initial `modulations` array into a sequence's `voice` or a step's `overrides` to modulate transport notes. Timed tones
|
|
150
|
+
Use `target: 'fmDepth'` only with `WAVE.SINE`; a filter target requires an active filter and PWM requires `WAVE.SQUARE`. Free rates are 0 to 20 Hz; synced subdivisions follow native transport BPM. Depth is normalized, with cutoff/PWM/FM contributions clamped to safe parameter ranges. `modulations: []` disables both slots; omitting the field on update preserves them. Copy the same initial `modulations` array into a sequence's `voice` or a step's `overrides` to modulate transport notes. Timed tones and chords do not accept this array; native sequences accept it in `voice` or step `overrides`.
|
|
151
151
|
|
|
152
152
|
## Measure peaks and limiter reduction
|
|
153
153
|
|
|
154
154
|
Enable diagnostics only while measuring and keep unavailable readings distinct from zero. A brief overload can disappear from the window meter while remaining in the accumulated maximum.
|
|
155
155
|
|
|
156
156
|
```javascript
|
|
157
|
-
if (!['
|
|
157
|
+
if (!['enableStats', 'disableStats', 'getStats', 'resetStats'].every(function (name) {
|
|
158
158
|
return typeof synth[name] === 'function'
|
|
159
159
|
})) throw new Error('Statistics APIs are required')
|
|
160
|
-
if (!synth.
|
|
160
|
+
if (!synth.enableStats()) throw new Error('Diagnostics rejected')
|
|
161
161
|
synth.resetStats()
|
|
162
162
|
var mixTimer = setInterval(function () {
|
|
163
163
|
var stats = synth.getStats()
|
|
@@ -167,8 +167,33 @@ var mixTimer = setInterval(function () {
|
|
|
167
167
|
|
|
168
168
|
function closeMixMeter () {
|
|
169
169
|
clearInterval(mixTimer)
|
|
170
|
-
if (!synth.
|
|
170
|
+
if (!synth.disableStats()) Ti.API.warn('Diagnostics disable rejected')
|
|
171
171
|
}
|
|
172
172
|
```
|
|
173
173
|
|
|
174
174
|
Call `closeMixMeter` when its owner hides or closes. Display unavailable or unmeasured fields as unavailable. Reset deliberately before a comparison. These values measure digital peak protection, not speaker loudness, subjective quality or a guarantee that every audio deadline was met.
|
|
175
|
+
|
|
176
|
+
## Bounded variation for repeated noise impacts
|
|
177
|
+
|
|
178
|
+
The 1.1.0 packages choose values once per admitted attack. This example keeps the noise source and envelope fixed, varies the initial noise/filter settings, and lets the authored sweep endpoints finish unchanged. Call it for actual gameplay events after normal engine startup. For periodic repetition, pass the finite recipe to `repeatTone` with `intervalMs` and retain its positive tone ID; use `stopTone` on cancellation.
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
var synth = require('ti.synthengine')
|
|
182
|
+
var contract = synth.getDefaults()
|
|
183
|
+
var impact = {
|
|
184
|
+
waveType: contract.waveTypes.NOISE,
|
|
185
|
+
frequency: 200, duration: 240, attack: 2, release: 170,
|
|
186
|
+
volume: 0.20, noiseRate: 5000, noiseRateEnd: 900,
|
|
187
|
+
filterType: 'lowpass', filterHz: 2800, filterHzEnd: 500, filterQ: 0.8
|
|
188
|
+
}
|
|
189
|
+
if (contract.capabilities && contract.capabilities.variation === true
|
|
190
|
+
&& contract.variationSchemaVersion === 1) {
|
|
191
|
+
impact.variation = {
|
|
192
|
+
amount: 0.6,
|
|
193
|
+
ranges: { noiseRate: [4200, 5800], filterHz: [2400, 3200] }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
synth.playTone(impact)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Omit `seed` for successive native selections. An explicit seed repeats the selected parameters of standalone calls. The [variation reference](native-variation.md) includes chord gain, sustained ownership, native loop inheritance and revision examples. Its amount is linear; Studio divides the displayed percentage by 100 to obtain the same value.
|