@maccesar/titools 5.2.5 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -37
- package/agents/ti-pro.md +1 -1
- package/commands/ti-audit.md +1 -1
- package/docs/examples.md +15 -13
- package/package.json +1 -1
- package/skills/ti-synthengine/SKILL.md +62 -111
- package/skills/ti-synthengine/assets/example/README.md +50 -59
- package/skills/ti-synthengine/assets/example/app.js +67 -65
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +39 -40
- package/skills/ti-synthengine/assets/example/lib/soundPreset.js +34 -0
- package/skills/ti-synthengine/assets/index.d.ts +122 -2
- package/skills/ti-synthengine/references/advanced-examples.md +4 -4
- package/skills/ti-synthengine/references/api.md +40 -18
- package/skills/ti-synthengine/references/audio-engine.md +15 -57
- package/skills/ti-synthengine/references/examples.md +63 -6
- package/skills/ti-synthengine/references/guide.md +61 -57
- package/skills/ti-synthengine/references/instruments-and-effects.md +15 -15
- package/skills/ti-synthengine/references/native-variation.md +53 -14
- package/skills/ti-synthengine/references/recipes-interaction.md +25 -20
- package/skills/ti-synthengine/references/recipes.md +19 -16
- package/skills/ti-synthengine/references/recommendations.md +12 -12
- package/skills/ti-synthengine/references/sound-api.md +197 -0
- package/skills/ti-synthengine/references/sound-design.md +7 -7
- package/skills/ti-synthengine/references/source-notes.md +62 -172
- package/skills/ti-synthengine/references/troubleshooting.md +17 -19
- package/skills/ti-synthengine/references/upgrade-2.0.0.md +40 -0
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +0 -162
- package/skills/ti-synthengine/references/audio-pipeline-validation.md +0 -19
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- 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 +0 -1918
- package/skills/ti-synthengine/references/native-tone-repetition-validation.md +0 -59
- package/skills/ti-synthengine/references/native-variation-proposal.md +0 -182
- package/skills/ti-synthengine/references/native-variation-validation.md +0 -63
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +0 -1146
- package/skills/ti-synthengine/references/physical-validation.json +0 -1457
- package/skills/ti-synthengine/references/source-snapshot.json +0 -542
- package/skills/ti-synthengine/references/stats-performance.json +0 -2992
- package/skills/ti-synthengine/references/stats-performance.md +0 -82
- package/skills/ti-synthengine/references/studio-validation.json +0 -6202
- package/skills/ti-synthengine/references/unified-bypass-performance.json +0 -22425
- package/skills/ti-synthengine/references/unified-validation.json +0 -314
|
@@ -1,97 +1,88 @@
|
|
|
1
|
-
> Source snapshot: official TiSynthEngine documentation at commit `
|
|
1
|
+
> Source snapshot: official TiSynthEngine documentation at commit `9d74aa45f84d3f2822d4aab76874efe37d81257f` (2026-11-03). Published module 2.0.0. Upstream `example/README.md` SHA-256: `db0f5921699859a1dd6918f215a4d30b008dd933cae458f21033216d7f807dd0`.
|
|
2
2
|
|
|
3
3
|
# Interactive example
|
|
4
4
|
|
|
5
|
-
The Classic Titanium app in [app.js](app.js) demonstrates the
|
|
5
|
+
The Classic Titanium app in [app.js](app.js) demonstrates the Sound object API on Android and iOS. It requires TiSynth 2.0.0, reported by `getContract().version`. `getDefaults().version` reports the same installed module version.
|
|
6
|
+
|
|
7
|
+
The default configuration is 16 voices, `conservative` mixing and the `playback` session. Master volume starts at 100% and optional diagnostics start off. Creating a Sound does not reserve a voice.
|
|
6
8
|
|
|
7
9
|
## Run it
|
|
8
10
|
|
|
9
|
-
1. Create a Classic Titanium application with Android and/or iOS support.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
11
|
+
1. Create a Classic Titanium application with Android and/or iOS support. For Alloy projects, keep the example in a separate Classic test app so the Alloy-generated files remain intact.
|
|
12
|
+
2. Build and install the current module source for the target platforms. Select the version declared by that build in `tiapp.xml`; rebuilding and reinstalling the test app is necessary after changing module packages.
|
|
13
|
+
3. Copy the entire `example/` directory into the app's `Resources/`, including `app.js` and every file under `lib/`. No images or audio files are needed.
|
|
14
|
+
4. Run with Titanium SDK 13.2.0.GA or newer, Android compile API 35 or newer, and iOS 15 or newer. See the [installation guide](../../references/guide.md#install-the-module).
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
<modules>
|
|
15
|
-
<module platform="android" version="1.1.0">ti.synthengine</module>
|
|
16
|
-
<module platform="iphone" version="1.1.0">ti.synthengine</module>
|
|
17
|
-
</modules>
|
|
18
|
-
```
|
|
16
|
+
The example checks the Sound contract, spatial limits and enable/disable statistics methods before constructing the screen. Audio starts after layout settles. If startup fails, session buttons remain available to retry. Closing the window retires its pattern listener, clears startup/statistics timers, disables diagnostics and shuts down the engine.
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
This example plays foreground finite sounds and patterns. Sustained notes, master processing and voice LFOs have separate examples in the [advanced documentation](../../references/advanced-examples.md#sustained-game-motor).
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
5. Build and run the test app with the Titanium CLI or your existing Titanium launch configuration. Use Titanium SDK 13.2.0.GA or newer, Android compile API 35 or newer, and iOS 15 or newer. See the [module installation guide](../../references/guide.md#install-the-module).
|
|
20
|
+
## Synthesis and sequencing
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
`createSound({ sound, playback })` separates synthesis properties from interpretation and timing. `lib/soundPreset.js` converts the example's editable recipes to this schema without playing audio. Finite sounds call `Sound.play()` and preserve their Boolean acceptance result. Chords declare `type: 'chord'`, including a chord containing just one note.
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
## Native finite-tone repetition
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
```js
|
|
27
|
+
var synth = require('ti.synthengine')
|
|
28
|
+
var shot = synth.createSound({
|
|
29
|
+
sound: { waveType: synth.getContract().waveTypes.SQUARE, attack: 2, release: 40, volume: 0.1 },
|
|
30
|
+
playback: { type: 'repeat', frequencies: [1380], frequencyEnd: 340, duration: 105, interval: 200 }
|
|
31
|
+
})
|
|
32
|
+
if (synth.start({ maxVoices: 16, mixingProfile: 'conservative' })) {
|
|
33
|
+
synth.setVolume(1)
|
|
34
|
+
var repetition = shot.play()
|
|
35
|
+
if (repetition) {
|
|
36
|
+
repetition.update({ interval: 150, volume: 0.08 })
|
|
37
|
+
// Later: repetition.stop()
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
30
41
|
|
|
31
|
-
-
|
|
32
|
-
- Try stereo pan, pitch sweep, vibrato, variable-rate noise, pulse width, custom wavetable and quantization.
|
|
33
|
-
- Compare atomic chords with explicit pans, matched arpeggios, tempo/swing and noise percussion.
|
|
34
|
-
- Hear decay/sustain, resonant filtering, FM bell/electric piano and detuned voices.
|
|
42
|
+
Finite `duration` includes release; envelope times and repetition `interval` are milliseconds. Per-call overrides do not change the Sound definition. Repetition controls keep private execution identity. An update preserves the next deadline and changes subsequent attacks; stopping cancels future attacks while admitted shots finish. There is no JavaScript attack timer. Close, session changes and STOP ALL also release or invalidate repetition ownership.
|
|
35
43
|
|
|
36
|
-
|
|
44
|
+
The screen retains its waveform, English/solfege pitch, stereo motion, vibrato, variable-rate noise, pulse-width, wavetable, quantization, chord, arpeggio, tempo/swing, percussion, decay/sustain, filter, FM and detune demonstrations. Master BOOST intentionally raises the bus above unity; 100% restores unity. Repeated taps can overlap voices and cause limiting or replacement.
|
|
37
45
|
|
|
38
|
-
##
|
|
46
|
+
## Native attack variation
|
|
39
47
|
|
|
40
|
-
|
|
48
|
+
ATTACK VARIATION retains its metal-strike recipe. When both `capabilities.variation === true` and `variationSchemaVersion === 1` are announced, the renderer chooses pitch, FM depth and level once per admitted attack. Otherwise the recipe omits variation. See the [variation guide](../../references/native-variation.md).
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
## Sequence completion and cancellation
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
`lib/sequenceScore.js` compiles note-name scores to millisecond onsets and explicit release onsets. `soundPreset.js` maps step spacing to `playback.interval` or `steps[].interval`, keeps `length`, and places synthesis in `sound` or `steps[].sound`. The native sample clock schedules every attack.
|
|
45
53
|
|
|
46
|
-
|
|
54
|
+
`lib/sequenceOwner.js` retains the pattern control returned by `Sound.play()`. Tap PATTERN END once to see `end: completed`. Tap repeatedly to request a stop, coalesce pending scores and publish only the newest complete score after the matching control emits `end`. Its callback receives `{ reason: 'completed' | 'stopped' }`, without native IDs. Late events from retired controls cannot change application ownership.
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
A completed pattern can leave audible releases or shared effect tails. STOP ALL cancels future attacks and fades active voices. Changing sessions or closing the example shuts down its engine and cancels pending application publication.
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
## Headphone positioning
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
Use stereo headphones to compare the nine noise positions individually. Horizontal coordinates run from -1 to 1; vertical coordinates run from 1 above to -1 below. Center still uses binaural processing. Elevation perception varies by listener and headphones.
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
2. Tap it twice quickly. The example requests stop for its accepted sequence, coalesces repeated taps to the latest complete score, and publishes it only after the matching retirement event frees capacity. Native `sequenceend` events report `{ sequenceId, reason: 'completed' | 'stopped' }`.
|
|
56
|
-
3. Selecting a session shuts down the engine and restarts it with the selected policy. Events already queued for JavaScript can still arrive.
|
|
57
|
-
4. STOP ALL releases active voices and cancels future attacks. Closing the example shuts down its engine.
|
|
62
|
+
STEREO MODE omits both spatial coordinates and uses horizontal pan alone. BINAURAL MODE restores coordinates for new events; an active voice retains its original position. These modes affect only this section. SPATIAL CHORD places a whole chord above/right, and SPATIAL PATTERN places three broadband hits using per-step coordinates. Binaural events omit stereo pan fields.
|
|
58
63
|
|
|
59
|
-
|
|
64
|
+
Output remains two channels on a front plane, without rear positioning, distance attenuation or head tracking. See the [integration guide](../../references/guide.md#integrate-binaural-in-a-2d-app).
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
## Audio session
|
|
62
67
|
|
|
63
|
-
|
|
68
|
+
PLAYBACK SESSION and AMBIENT SESSION shut down the existing stream, cancel pending patterns, start the selected policy and restore master volume. They do not resume old sounds; a second `start()` alone cannot change a running session's policy.
|
|
64
69
|
|
|
65
|
-
-
|
|
66
|
-
- TOGGLE DIAGNOSTICS enables callback timing and signal meters, refreshing at 5 Hz. Toggle it off to stop the timer and optional measurements; basic counters remain available.
|
|
67
|
-
- RESET STATS clears accumulated statistics without stopping voices, cancelling queued work or changing the diagnostic switch. The next callback applies render-owned resets.
|
|
70
|
+
On physical iOS hardware, compare another app's music, silent mode and an output-route change. Ambient respects silent mode and mixes with other apps; playback ignores silent mode and normally interrupts them. Android accepts both strings without changing its output. Simulator results do not establish physical interruption or route behavior.
|
|
68
71
|
|
|
69
|
-
|
|
72
|
+
## Statistics
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
READ STATS takes a snapshot without resetting counters or enabling diagnostics. TOGGLE DIAGNOSTICS enables callback timing and signal meters, refreshing at 5 Hz; disabling it stops the timer and optional measurements while retaining basic counters. RESET STATS clears accumulated statistics without stopping voices or cancelling scheduled work.
|
|
72
75
|
|
|
73
|
-
The
|
|
76
|
+
The panel shows active/peak/spatial/tail voices, pending commands, replacements, request/publication rejections, priority drops, callback load, stereo peak and backend underruns. Unavailable measurements remain `n/a`, distinct from zero. Signal windows need about 200 ms after enabling diagnostics. See the [field reference](../../references/api.md#getstats).
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
Render load is callback elapsed time divided by the budget for that block. It is not total app CPU. The stream continues rendering silence with zero voices. Compare measurements on the same device, route, configuration and load; a clean counter does not establish audio quality. Turn diagnostics off before leaving this foreground example in the background.
|
|
76
79
|
|
|
77
80
|
## Checks and limits
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
From the module repository root:
|
|
80
83
|
|
|
81
84
|
```sh
|
|
82
|
-
node tests/
|
|
83
|
-
node --test tests/example_app_regression.js
|
|
85
|
+
node --test tests/example_app_regression.js tests/sequence_owner_regression.js
|
|
84
86
|
```
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
## Native attack variation
|
|
90
|
-
|
|
91
|
-
The ATTACK VARIATION button submits the same metal-strike recipe on each press. With `capabilities.variation === true` and `variationSchemaVersion === 1`, the renderer chooses pitch, FM depth and level once per admitted attack. Older packages receive the original recipe without the dictionary. This button uses independent timed events; the [variation guide](../../references/native-variation.md) separately demonstrates sustained ownership and native sequence retriggers. Physical listening acceptance remains separate from host correctness checks.
|
|
92
|
-
|
|
93
|
-
## Native finite-tone repetition
|
|
94
|
-
|
|
95
|
-
START REPEATING calls `repeatTone` once with a complete 105 ms square-wave shot and required `intervalMs: 200`. It retains the positive tone ID. EDIT REPEATING calls `updateTone` for that ID, keeping the next deadline and changing subsequent separation to 150 ms. STOP REPEATING calls `stopTone`, cancelling future attacks while admitted shots finish. Close, session changes and global Stop also release or invalidate ownership. There is no JavaScript attack timer for this demo.
|
|
96
|
-
|
|
97
|
-
`playTone` remains a standalone Boolean operation and rejects `intervalMs`; `repeatTone` always returns a numeric ID or 0 and requires the interval. Independent JavaScript calls remain supported, including finite bursts scheduled by an application's own event clock. See [the full contract and standalone burst example](../../references/api.md#finite-tone-repetition).
|
|
88
|
+
These checks execute the real interactive callbacks and the current public adapter over native payload spies. They cover preserved synthesis parameters, rejection feedback, spatial bypass, repeated finite shots, session/gain restoration, diagnostics, native retirement coalescing, active updates, late events and close-before-start. Native validator replay and platform listening acceptance remain separate. Host checks do not establish layout, perceived elevation, silent-mode behavior or sound quality.
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
+
var SoundPreset = require('lib/soundPreset')
|
|
1
2
|
var sequenceScore = require('lib/sequenceScore')
|
|
2
3
|
var Synth = require('ti.synthengine')
|
|
3
4
|
|
|
4
5
|
var sequenceOwner = require('lib/sequenceOwner')(Synth, {
|
|
5
|
-
rejected: function() { setStatus('Request rejected · sequence publication', COLORS.red) }
|
|
6
|
+
rejected: function() { setStatus('Request rejected · sequence publication', COLORS.red) },
|
|
7
|
+
ended: onSequenceEnd
|
|
6
8
|
})
|
|
7
9
|
function publishSequence(options) { return sequenceOwner.submit(options) }
|
|
8
10
|
|
|
9
|
-
var defaults = Synth.
|
|
10
|
-
if (!defaults || defaults.
|
|
11
|
-
throw new Error('TiSynthEngine
|
|
11
|
+
var defaults = Synth.getContract()
|
|
12
|
+
if (!defaults || defaults.version !== '2.0.0') {
|
|
13
|
+
throw new Error('TiSynthEngine 2.0.0 is required')
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
if (!defaults.limits || !defaults.limits.spatialX || !defaults.limits.spatialY
|
|
15
17
|
|| typeof Synth.getStats !== 'function' || typeof Synth.resetStats !== 'function'
|
|
16
18
|
|| typeof Synth.enableStats !== 'function' || typeof Synth.disableStats !== 'function'
|
|
17
|
-
|| typeof Synth.
|
|
18
|
-
throw new Error('Install the current TiSynthEngine
|
|
19
|
+
|| typeof Synth.createSound !== 'function') {
|
|
20
|
+
throw new Error('Install the current TiSynthEngine Sound API packages, then rebuild and reinstall this app')
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
var WAVE = defaults.waveTypes
|
|
@@ -24,7 +26,11 @@ var engineRunning = false
|
|
|
24
26
|
var startTimer = null
|
|
25
27
|
var statsTimer = null
|
|
26
28
|
var diagnosticsEnabled = false
|
|
27
|
-
var
|
|
29
|
+
var repeatingTone = null
|
|
30
|
+
var repeatingSound = Synth.createSound({
|
|
31
|
+
sound: { attack: 2, release: 40, waveType: WAVE.SQUARE, volume: 0.1 },
|
|
32
|
+
playback: { type: 'repeat', frequencies: [1380], frequencyEnd: 340, duration: 105, interval: 200 }
|
|
33
|
+
})
|
|
28
34
|
var selectedSession = 'playback'
|
|
29
35
|
var masterVolume = 1
|
|
30
36
|
var binauralEnabled = true
|
|
@@ -202,7 +208,7 @@ function makeHero() {
|
|
|
202
208
|
height: Ti.UI.SIZE,
|
|
203
209
|
top: dp(16)
|
|
204
210
|
})
|
|
205
|
-
badgeRow.add(makeBadge('API
|
|
211
|
+
badgeRow.add(makeBadge('API 2.0.0', COLORS.indigo))
|
|
206
212
|
badgeRow.add(makeBadge('ANDROID + iOS', COLORS.surfaceMuted))
|
|
207
213
|
hero.add(badgeRow)
|
|
208
214
|
hero.add(statusPill)
|
|
@@ -327,14 +333,14 @@ function makeCompactAction(options) {
|
|
|
327
333
|
}
|
|
328
334
|
|
|
329
335
|
function playMatchedSine(note) {
|
|
330
|
-
return Synth.
|
|
336
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
331
337
|
note: note,
|
|
332
338
|
duration: 600,
|
|
333
339
|
attack: 10,
|
|
334
340
|
release: 80,
|
|
335
341
|
volume: 0.7,
|
|
336
342
|
waveType: WAVE.SINE
|
|
337
|
-
})
|
|
343
|
+
})).play()
|
|
338
344
|
}
|
|
339
345
|
|
|
340
346
|
function playMatchedArpeggio(pitchKey, pitches) {
|
|
@@ -394,18 +400,18 @@ waveNames.forEach(function (name, index) {
|
|
|
394
400
|
waveType: waveTypes[index]
|
|
395
401
|
}
|
|
396
402
|
if (waveTypes[index] === WAVE.WAVETABLE) options.waveTable = STEPPED_WAVE
|
|
397
|
-
return Synth.
|
|
403
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', options)).play()
|
|
398
404
|
}
|
|
399
405
|
}))
|
|
400
406
|
})
|
|
401
407
|
waveSection.add(Ti.UI.createView({ height: dp(12) }))
|
|
402
408
|
scrollView.add(waveSection)
|
|
403
409
|
|
|
404
|
-
// A repeated finite recipe
|
|
410
|
+
// A repeated finite recipe retains its own control; there is no attack timer.
|
|
405
411
|
function stopRepeatingTone() {
|
|
406
|
-
if (!
|
|
407
|
-
if (!
|
|
408
|
-
|
|
412
|
+
if (!repeatingTone) return true
|
|
413
|
+
if (!repeatingTone.stop()) return false
|
|
414
|
+
repeatingTone = null
|
|
409
415
|
return true
|
|
410
416
|
}
|
|
411
417
|
var repeatSection = makeSection('Finite-tone repetition',
|
|
@@ -414,19 +420,17 @@ var repeatSection = makeSection('Finite-tone repetition',
|
|
|
414
420
|
repeatSection.add(makeAction({
|
|
415
421
|
title: 'START REPEATING', color: COLORS.teal, pressedColor: COLORS.tealPressed,
|
|
416
422
|
onPress: function () {
|
|
417
|
-
if (
|
|
418
|
-
var
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
if (!Number.isInteger(id) || id <= 0) return false
|
|
422
|
-
repeatingToneId = id
|
|
423
|
+
if (repeatingTone) return true
|
|
424
|
+
var control = repeatingSound.play()
|
|
425
|
+
if (!control) return false
|
|
426
|
+
repeatingTone = control
|
|
423
427
|
return true
|
|
424
428
|
}
|
|
425
429
|
}))
|
|
426
430
|
repeatSection.add(makeAction({
|
|
427
431
|
title: 'EDIT REPEATING', color: COLORS.teal, pressedColor: COLORS.tealPressed,
|
|
428
432
|
onPress: function () {
|
|
429
|
-
return !!
|
|
433
|
+
return !!repeatingTone && repeatingTone.update({ interval: 150, volume: 0.08 })
|
|
430
434
|
}
|
|
431
435
|
}))
|
|
432
436
|
repeatSection.add(makeAction({
|
|
@@ -488,7 +492,7 @@ motionSection.add(makeAction({
|
|
|
488
492
|
pressedColor: COLORS.tealPressed,
|
|
489
493
|
status: 'Pan sweep · left to right',
|
|
490
494
|
onPress: function () {
|
|
491
|
-
return Synth.
|
|
495
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
492
496
|
frequency: 440,
|
|
493
497
|
duration: 2000,
|
|
494
498
|
attack: 20,
|
|
@@ -496,7 +500,7 @@ motionSection.add(makeAction({
|
|
|
496
500
|
volume: 0.8,
|
|
497
501
|
pan: -1.0,
|
|
498
502
|
panEnd: 1.0
|
|
499
|
-
})
|
|
503
|
+
})).play()
|
|
500
504
|
}
|
|
501
505
|
}))
|
|
502
506
|
motionSection.add(makeAction({
|
|
@@ -505,7 +509,7 @@ motionSection.add(makeAction({
|
|
|
505
509
|
pressedColor: COLORS.pinkPressed,
|
|
506
510
|
status: 'Pitch sweep · 200 to 900 Hz',
|
|
507
511
|
onPress: function () {
|
|
508
|
-
return Synth.
|
|
512
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
509
513
|
frequency: 200,
|
|
510
514
|
frequencyEnd: 900,
|
|
511
515
|
duration: 400,
|
|
@@ -513,7 +517,7 @@ motionSection.add(makeAction({
|
|
|
513
517
|
release: 40,
|
|
514
518
|
volume: 0.7,
|
|
515
519
|
waveType: WAVE.SAWTOOTH
|
|
516
|
-
})
|
|
520
|
+
})).play()
|
|
517
521
|
}
|
|
518
522
|
}))
|
|
519
523
|
motionSection.add(makeAction({
|
|
@@ -522,7 +526,7 @@ motionSection.add(makeAction({
|
|
|
522
526
|
pressedColor: COLORS.orangePressed,
|
|
523
527
|
status: 'LFO vibrato · 5 Hz',
|
|
524
528
|
onPress: function () {
|
|
525
|
-
return Synth.
|
|
529
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
526
530
|
frequency: 440,
|
|
527
531
|
duration: 2000,
|
|
528
532
|
attack: 20,
|
|
@@ -530,7 +534,7 @@ motionSection.add(makeAction({
|
|
|
530
534
|
volume: 0.7,
|
|
531
535
|
lfoFreq: 5,
|
|
532
536
|
lfoDepth: 30
|
|
533
|
-
})
|
|
537
|
+
})).play()
|
|
534
538
|
}
|
|
535
539
|
}))
|
|
536
540
|
motionSection.add(Ti.UI.createView({ height: dp(16) }))
|
|
@@ -551,7 +555,7 @@ retroSection.add(makeAction({
|
|
|
551
555
|
pressedColor: COLORS.brownPressed,
|
|
552
556
|
status: 'Filtered sample-and-hold noise sweep',
|
|
553
557
|
onPress: function () {
|
|
554
|
-
return Synth.
|
|
558
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
555
559
|
frequency: 120,
|
|
556
560
|
duration: 760,
|
|
557
561
|
attack: 1,
|
|
@@ -562,7 +566,7 @@ retroSection.add(makeAction({
|
|
|
562
566
|
noiseRateEnd: 650,
|
|
563
567
|
lowPassHz: 2500,
|
|
564
568
|
bitDepth: 8
|
|
565
|
-
})
|
|
569
|
+
})).play()
|
|
566
570
|
}
|
|
567
571
|
}))
|
|
568
572
|
retroSection.add(makeAction({
|
|
@@ -571,7 +575,7 @@ retroSection.add(makeAction({
|
|
|
571
575
|
pressedColor: COLORS.orangePressed,
|
|
572
576
|
status: 'Square pulse-width sweep',
|
|
573
577
|
onPress: function () {
|
|
574
|
-
return Synth.
|
|
578
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
575
579
|
note: 'A3',
|
|
576
580
|
duration: 700,
|
|
577
581
|
attack: 3,
|
|
@@ -580,7 +584,7 @@ retroSection.add(makeAction({
|
|
|
580
584
|
waveType: WAVE.SQUARE,
|
|
581
585
|
pulseWidth: 0.1,
|
|
582
586
|
pulseWidthEnd: 0.86
|
|
583
|
-
})
|
|
587
|
+
})).play()
|
|
584
588
|
}
|
|
585
589
|
}))
|
|
586
590
|
retroSection.add(makeAction({
|
|
@@ -589,7 +593,7 @@ retroSection.add(makeAction({
|
|
|
589
593
|
pressedColor: COLORS.tealPressed,
|
|
590
594
|
status: 'Custom stepped wavetable',
|
|
591
595
|
onPress: function () {
|
|
592
|
-
return Synth.
|
|
596
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
593
597
|
note: 'D4',
|
|
594
598
|
duration: 680,
|
|
595
599
|
attack: 4,
|
|
@@ -598,7 +602,7 @@ retroSection.add(makeAction({
|
|
|
598
602
|
waveType: WAVE.WAVETABLE,
|
|
599
603
|
waveTable: STEPPED_WAVE,
|
|
600
604
|
bitDepth: 8
|
|
601
|
-
})
|
|
605
|
+
})).play()
|
|
602
606
|
}
|
|
603
607
|
}))
|
|
604
608
|
retroSection.add(makeAction({
|
|
@@ -658,14 +662,14 @@ harmonySection.add(makeAction({
|
|
|
658
662
|
pressedColor: COLORS.blue,
|
|
659
663
|
status: 'C major triad · sine',
|
|
660
664
|
onPress: function () {
|
|
661
|
-
return Synth.
|
|
665
|
+
return Synth.createSound(SoundPreset.fromRecipe('chord', {
|
|
662
666
|
frequencies: [261.63, 329.63, 392.0],
|
|
663
667
|
duration: 1200,
|
|
664
668
|
attack: 20,
|
|
665
669
|
release: 300,
|
|
666
670
|
volume: 0.7,
|
|
667
671
|
panSpread: 0.8
|
|
668
|
-
})
|
|
672
|
+
})).play()
|
|
669
673
|
}
|
|
670
674
|
}))
|
|
671
675
|
harmonySection.add(makeAction({
|
|
@@ -674,7 +678,7 @@ harmonySection.add(makeAction({
|
|
|
674
678
|
pressedColor: COLORS.indigoPressed,
|
|
675
679
|
status: 'C major chord · square and wide',
|
|
676
680
|
onPress: function () {
|
|
677
|
-
return Synth.
|
|
681
|
+
return Synth.createSound(SoundPreset.fromRecipe('chord', {
|
|
678
682
|
notes: ['C4', 'E4', 'G4', 'C5'],
|
|
679
683
|
duration: 1000,
|
|
680
684
|
attack: 10,
|
|
@@ -682,7 +686,7 @@ harmonySection.add(makeAction({
|
|
|
682
686
|
volume: 0.6,
|
|
683
687
|
waveType: WAVE.SQUARE,
|
|
684
688
|
pans: [-0.9, -0.3, 0.3, 0.9]
|
|
685
|
-
})
|
|
689
|
+
})).play()
|
|
686
690
|
}
|
|
687
691
|
}))
|
|
688
692
|
harmonySection.add(Ti.UI.createView({ height: dp(16) }))
|
|
@@ -776,7 +780,7 @@ expressiveSection.add(makeAction({
|
|
|
776
780
|
pressedColor: COLORS.greenPressed,
|
|
777
781
|
status: 'Pluck · decay and sustain',
|
|
778
782
|
onPress: function () {
|
|
779
|
-
return Synth.
|
|
783
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
780
784
|
note: 'E4',
|
|
781
785
|
duration: 900,
|
|
782
786
|
attack: 2,
|
|
@@ -785,7 +789,7 @@ expressiveSection.add(makeAction({
|
|
|
785
789
|
release: 380,
|
|
786
790
|
volume: 0.6,
|
|
787
791
|
waveType: WAVE.TRIANGLE
|
|
788
|
-
})
|
|
792
|
+
})).play()
|
|
789
793
|
}
|
|
790
794
|
}))
|
|
791
795
|
expressiveSection.add(makeAction({
|
|
@@ -794,7 +798,7 @@ expressiveSection.add(makeAction({
|
|
|
794
798
|
pressedColor: COLORS.orangePressed,
|
|
795
799
|
status: 'Wah · band-pass cutoff sweep',
|
|
796
800
|
onPress: function () {
|
|
797
|
-
return Synth.
|
|
801
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
798
802
|
note: 'A2',
|
|
799
803
|
duration: 1000,
|
|
800
804
|
attack: 10,
|
|
@@ -805,7 +809,7 @@ expressiveSection.add(makeAction({
|
|
|
805
809
|
filterHz: 350,
|
|
806
810
|
filterHzEnd: 2400,
|
|
807
811
|
filterQ: 5
|
|
808
|
-
})
|
|
812
|
+
})).play()
|
|
809
813
|
}
|
|
810
814
|
}))
|
|
811
815
|
expressiveSection.add(makeAction({
|
|
@@ -814,7 +818,7 @@ expressiveSection.add(makeAction({
|
|
|
814
818
|
pressedColor: COLORS.indigoPressed,
|
|
815
819
|
status: 'Bell · FM index decays',
|
|
816
820
|
onPress: function () {
|
|
817
|
-
return Synth.
|
|
821
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
818
822
|
note: 'C5',
|
|
819
823
|
duration: 2600,
|
|
820
824
|
attack: 1,
|
|
@@ -826,7 +830,7 @@ expressiveSection.add(makeAction({
|
|
|
826
830
|
fmRatio: 3.5,
|
|
827
831
|
fmDepth: 4,
|
|
828
832
|
fmDepthEnd: 0
|
|
829
|
-
})
|
|
833
|
+
})).play()
|
|
830
834
|
}
|
|
831
835
|
}))
|
|
832
836
|
expressiveSection.add(makeAction({
|
|
@@ -835,7 +839,7 @@ expressiveSection.add(makeAction({
|
|
|
835
839
|
pressedColor: COLORS.blue,
|
|
836
840
|
status: 'Electric piano · FM index mellows',
|
|
837
841
|
onPress: function () {
|
|
838
|
-
return Synth.
|
|
842
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', {
|
|
839
843
|
note: 'E3',
|
|
840
844
|
duration: 1800,
|
|
841
845
|
attack: 3,
|
|
@@ -847,7 +851,7 @@ expressiveSection.add(makeAction({
|
|
|
847
851
|
fmRatio: 1,
|
|
848
852
|
fmDepth: 2.2,
|
|
849
853
|
fmDepthEnd: 0.25
|
|
850
|
-
})
|
|
854
|
+
})).play()
|
|
851
855
|
}
|
|
852
856
|
}))
|
|
853
857
|
expressiveSection.add(makeAction({
|
|
@@ -856,7 +860,7 @@ expressiveSection.add(makeAction({
|
|
|
856
860
|
pressedColor: COLORS.pinkPressed,
|
|
857
861
|
status: 'Thick chord · detuned voices',
|
|
858
862
|
onPress: function () {
|
|
859
|
-
return Synth.
|
|
863
|
+
return Synth.createSound(SoundPreset.fromRecipe('chord', {
|
|
860
864
|
notes: ['A3', 'E4', 'A3', 'E4', 'A3', 'E4'],
|
|
861
865
|
detunes: [-9, -9, 0, 0, 9, 9],
|
|
862
866
|
duration: 3200,
|
|
@@ -867,7 +871,7 @@ expressiveSection.add(makeAction({
|
|
|
867
871
|
filterType: 'lowpass',
|
|
868
872
|
filterHz: 1500,
|
|
869
873
|
panSpread: 0.8
|
|
870
|
-
})
|
|
874
|
+
})).play()
|
|
871
875
|
}
|
|
872
876
|
}))
|
|
873
877
|
|
|
@@ -887,11 +891,11 @@ expressiveSection.add(makeAction({
|
|
|
887
891
|
volume: [0.291, 0.3], fmDepth: [2.944, 3.456], pitchCents: [-12, 12]
|
|
888
892
|
} }
|
|
889
893
|
}
|
|
890
|
-
return Synth.
|
|
894
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', options)).play()
|
|
891
895
|
}
|
|
892
896
|
}))
|
|
893
897
|
|
|
894
|
-
var patternEndLabel = makeText('
|
|
898
|
+
var patternEndLabel = makeText('end: no pattern yet', {
|
|
895
899
|
top: dp(8),
|
|
896
900
|
left: dp(16),
|
|
897
901
|
right: dp(16),
|
|
@@ -901,21 +905,20 @@ var patternEndLabel = makeText('sequenceend: no pattern yet', {
|
|
|
901
905
|
|
|
902
906
|
function onSequenceEnd(event) {
|
|
903
907
|
patternEndLabel.text = event.reason === 'completed'
|
|
904
|
-
? '
|
|
905
|
-
: '
|
|
908
|
+
? 'end: completed'
|
|
909
|
+
: 'end: stopped'
|
|
906
910
|
setStatus(patternEndLabel.text, event.reason === 'completed' ? COLORS.green : COLORS.yellow)
|
|
907
911
|
}
|
|
908
912
|
|
|
909
|
-
Synth.addEventListener('sequenceend', onSequenceEnd)
|
|
910
913
|
|
|
911
914
|
expressiveSection.add(makeAction({
|
|
912
915
|
title: 'PATTERN END\nTap once to finish; tap again quickly to replace',
|
|
913
916
|
color: COLORS.yellow,
|
|
914
917
|
pressedColor: COLORS.yellowPressed,
|
|
915
918
|
textColor: COLORS.canvas,
|
|
916
|
-
status: 'Pattern running · waiting for
|
|
919
|
+
status: 'Pattern running · waiting for end',
|
|
917
920
|
onPress: function () {
|
|
918
|
-
patternEndLabel.text = '
|
|
921
|
+
patternEndLabel.text = 'end: pattern running'
|
|
919
922
|
return publishSequence(sequenceScore({
|
|
920
923
|
bpm: 132,
|
|
921
924
|
noteValue: '8n',
|
|
@@ -984,10 +987,10 @@ spatialPoints.forEach(function (point, index) {
|
|
|
984
987
|
title: point[0] + '\nNoise burst',
|
|
985
988
|
fontSize: 11, color: COLORS.teal, pressedColor: COLORS.tealPressed,
|
|
986
989
|
onPress: function () {
|
|
987
|
-
return Synth.
|
|
990
|
+
return Synth.createSound(SoundPreset.fromRecipe('tone', Object.assign({
|
|
988
991
|
frequency: 200, waveType: WAVE.NOISE, duration: 550,
|
|
989
992
|
attack: 5, release: 200, volume: 0.3
|
|
990
|
-
}, placement(point[1], point[2])))
|
|
993
|
+
}, placement(point[1], point[2])))).play()
|
|
991
994
|
}
|
|
992
995
|
}))
|
|
993
996
|
})
|
|
@@ -996,10 +999,10 @@ spatialSection.add(makeAction({
|
|
|
996
999
|
color: COLORS.blueDark, pressedColor: COLORS.blue,
|
|
997
1000
|
onPress: function () {
|
|
998
1001
|
var position = binauralEnabled ? { spatialX: 0.7, spatialY: 1 } : { panSpread: 0, pans: [0.7, 0.7, 0.7] }
|
|
999
|
-
return Synth.
|
|
1002
|
+
return Synth.createSound(SoundPreset.fromRecipe('chord', Object.assign({
|
|
1000
1003
|
notes: ['C4', 'E4', 'G4'], duration: 1200, attack: 15, release: 300,
|
|
1001
1004
|
volume: 0.4, waveType: WAVE.SAWTOOTH
|
|
1002
|
-
}, position))
|
|
1005
|
+
}, position))).play()
|
|
1003
1006
|
}
|
|
1004
1007
|
}))
|
|
1005
1008
|
spatialSection.add(makeAction({
|
|
@@ -1035,10 +1038,10 @@ var sessionLabel = makeText('Session: playback', { left: dp(16), right: dp(16),
|
|
|
1035
1038
|
color: COLORS.blueDark, pressedColor: COLORS.blue,
|
|
1036
1039
|
accessibilityHint: 'Restarts the engine with this session policy',
|
|
1037
1040
|
onPress: function () {
|
|
1038
|
-
|
|
1041
|
+
repeatingTone = null; sequenceOwner.reset(); Synth.shutdown()
|
|
1039
1042
|
engineRunning = false
|
|
1040
1043
|
selectedSession = session
|
|
1041
|
-
patternEndLabel.text = '
|
|
1044
|
+
patternEndLabel.text = 'end: cancelled by shutdown (no event)'
|
|
1042
1045
|
return startAppCycles()
|
|
1043
1046
|
}
|
|
1044
1047
|
}))
|
|
@@ -1142,7 +1145,7 @@ boostRow.add(makeGridAction({
|
|
|
1142
1145
|
status: 'Future attacks cancelled; active voices fading',
|
|
1143
1146
|
accessibilityHint: 'Cancels future attacks and fades current voices',
|
|
1144
1147
|
onPress: function () {
|
|
1145
|
-
|
|
1148
|
+
repeatingTone = null; sequenceOwner.stop(); Synth.stopAll()
|
|
1146
1149
|
}
|
|
1147
1150
|
}))
|
|
1148
1151
|
controlSection.add(boostRow)
|
|
@@ -1164,7 +1167,7 @@ win.add(scrollView)
|
|
|
1164
1167
|
// ---------------------------------------------------------------------------
|
|
1165
1168
|
|
|
1166
1169
|
function setMasterVolume(volume) {
|
|
1167
|
-
if (!Synth.setVolume(
|
|
1170
|
+
if (!Synth.setVolume(volume)) return false
|
|
1168
1171
|
masterVolume = volume
|
|
1169
1172
|
return true
|
|
1170
1173
|
}
|
|
@@ -1210,7 +1213,6 @@ function cleanup() {
|
|
|
1210
1213
|
stopStatsPolling()
|
|
1211
1214
|
Synth.disableStats()
|
|
1212
1215
|
diagnosticsEnabled = false
|
|
1213
|
-
Synth.removeEventListener('sequenceend', onSequenceEnd)
|
|
1214
1216
|
win.removeEventListener('postlayout', onFirstPostLayout)
|
|
1215
1217
|
win.removeEventListener('open', hideAndroidActionBar)
|
|
1216
1218
|
win.removeEventListener('focus', hideAndroidActionBar)
|
|
@@ -1221,7 +1223,7 @@ function cleanup() {
|
|
|
1221
1223
|
startTimer = null
|
|
1222
1224
|
}
|
|
1223
1225
|
if (engineRunning) {
|
|
1224
|
-
|
|
1226
|
+
repeatingTone = null; sequenceOwner.reset(); Synth.shutdown()
|
|
1225
1227
|
engineRunning = false
|
|
1226
1228
|
}
|
|
1227
1229
|
}
|