@maccesar/titools 5.2.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CONTRIBUTING.md +69 -0
  2. package/README.md +80 -72
  3. package/agents/ti-pro.md +1 -1
  4. package/commands/ti-audit.md +2 -2
  5. package/docs/examples.md +91 -0
  6. package/docs/knowledge-index.md +57 -0
  7. package/lib/downloader.js +1 -1
  8. package/package.json +4 -2
  9. package/skills/purgetss/references/multi-density-images.md +1 -1
  10. package/skills/ti-admob-unified/SKILL.md +21 -7
  11. package/skills/ti-admob-unified/assets/example/app.js +229 -25
  12. package/skills/ti-admob-unified/assets/example/lib/ads.js +30 -2
  13. package/skills/ti-admob-unified/assets/example/library-usage.js +33 -1
  14. package/skills/ti-admob-unified/references/api-reference.md +39 -16
  15. package/skills/ti-admob-unified/references/banners.md +9 -9
  16. package/skills/ti-admob-unified/references/contributing.md +14 -9
  17. package/skills/ti-admob-unified/references/dependencies.md +31 -25
  18. package/skills/ti-admob-unified/references/fullscreen.md +43 -28
  19. package/skills/ti-admob-unified/references/game-monetization.md +138 -7
  20. package/skills/ti-admob-unified/references/getting-started.md +75 -31
  21. package/skills/ti-admob-unified/references/index.md +5 -2
  22. package/skills/ti-admob-unified/references/integration.md +8 -6
  23. package/skills/ti-admob-unified/references/library.md +41 -10
  24. package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
  25. package/skills/ti-admob-unified/references/mediation.md +22 -9
  26. package/skills/ti-admob-unified/references/migration.md +11 -7
  27. package/skills/ti-admob-unified/references/module-changelog.md +41 -11
  28. package/skills/ti-admob-unified/references/privacy.md +35 -8
  29. package/skills/ti-admob-unified/references/production.md +67 -0
  30. package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
  31. package/skills/ti-admob-unified/references/roadmap.md +28 -31
  32. package/skills/ti-admob-unified/references/source-notes.md +51 -4
  33. package/skills/ti-admob-unified/references/source-snapshot.json +249 -45
  34. package/skills/ti-admob-unified/references/third-party-notices.md +4 -4
  35. package/skills/ti-game/SKILL.md +23 -6
  36. package/skills/ti-game/references/api.md +34 -30
  37. package/skills/ti-game/references/debugging-performance.md +18 -9
  38. package/skills/ti-game/references/examples.md +103 -20
  39. package/skills/ti-game/references/input.md +101 -0
  40. package/skills/ti-game/references/patterns.md +39 -15
  41. package/skills/ti-game/references/physics.md +80 -52
  42. package/skills/ti-game/references/project-setup.md +118 -56
  43. package/skills/ti-game/references/recipes-action.md +107 -8
  44. package/skills/ti-game/references/recipes-adventure-effects.md +41 -9
  45. package/skills/ti-game/references/roadmap.md +10 -3
  46. package/skills/ti-game/references/source-notes.md +81 -0
  47. package/skills/ti-game/references/tilemaps.md +12 -4
  48. package/skills/ti-synthengine/SKILL.md +81 -95
  49. package/skills/ti-synthengine/assets/example/README.md +84 -0
  50. package/skills/ti-synthengine/assets/example/app.js +1178 -0
  51. package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
  52. package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
  53. package/skills/ti-synthengine/references/advanced-examples.md +174 -0
  54. package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
  55. package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
  56. package/skills/ti-synthengine/references/api.md +434 -380
  57. package/skills/ti-synthengine/references/audio-engine.md +157 -230
  58. package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
  59. package/skills/ti-synthengine/references/examples.md +123 -253
  60. package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
  61. package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
  62. package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
  63. package/skills/ti-synthengine/references/guide.md +188 -156
  64. package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
  65. package/skills/ti-synthengine/references/performance-review.json +1146 -0
  66. package/skills/ti-synthengine/references/physical-validation.json +1457 -0
  67. package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
  68. package/skills/ti-synthengine/references/recipes.md +423 -599
  69. package/skills/ti-synthengine/references/recommendations.md +63 -113
  70. package/skills/ti-synthengine/references/sound-design.md +134 -258
  71. package/skills/ti-synthengine/references/source-notes.md +93 -105
  72. package/skills/ti-synthengine/references/source-snapshot.json +379 -32
  73. package/skills/ti-synthengine/references/stats-performance.md +64 -20
  74. package/skills/ti-synthengine/references/studio-validation.json +6202 -0
  75. package/skills/ti-synthengine/references/troubleshooting.md +109 -121
  76. package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
  77. package/skills/ti-synthengine/references/unified-validation.json +314 -0
  78. package/AGENTS-VERCEL-RESEARCH.md +0 -175
@@ -0,0 +1,83 @@
1
+ // One application owner, with latest-request coalescing across native retirement.
2
+ // A stop is accepted before its slot becomes reusable. Only sequenceend permits
3
+ // the next complete publication; JavaScript never schedules individual notes.
4
+ module.exports = function createSequenceOwner(synth, options) {
5
+ options = options || {}
6
+ var current = 0, pending = null, stopping = false, listening = false
7
+ function listen() {
8
+ if (!listening) {
9
+ synth.addEventListener('sequenceend', ended)
10
+ listening = true
11
+ }
12
+ }
13
+ function rejected(method, payload) {
14
+ if (options.rejected) options.rejected(method, payload)
15
+ return false
16
+ }
17
+ function publish() {
18
+ if (!pending) return true
19
+ var payload = pending
20
+ pending = null
21
+ var id = synth.playSequence(payload)
22
+ if (!Number.isInteger(id) || id <= 0) return rejected('playSequence', payload)
23
+ current = id
24
+ if (options.published) options.published(id, payload)
25
+ return true
26
+ }
27
+ function ended(event) {
28
+ if (event.sequenceId !== current) return
29
+ current = 0
30
+ stopping = false
31
+ if (pending) publish()
32
+ if (options.ended) options.ended(event)
33
+ }
34
+ function requestStop() {
35
+ if (!current || stopping) return true
36
+ stopping = true
37
+ if (synth.stopSequence({ sequenceId: current })) return true
38
+ // A naturally retired ID can already have its completion queued for JS.
39
+ // Its slot still needs event draining before a replacement publication.
40
+ if (synth.getSequenceState({ sequenceId: current }) === null) return true
41
+ stopping = false
42
+ return rejected('stopSequence', { sequenceId: current })
43
+ }
44
+ return {
45
+ submit: function(payload) {
46
+ listen()
47
+ pending = payload
48
+ if (!current) return publish()
49
+ if (!requestStop()) {
50
+ pending = null
51
+ return false
52
+ }
53
+ return true
54
+ },
55
+ update: function(changes) {
56
+ if (pending) {
57
+ if (changes.steps !== undefined) {
58
+ pending = changes
59
+ return true
60
+ }
61
+ var voice = changes.voice ? Object.assign({}, pending.voice || {}, changes.voice) : pending.voice
62
+ pending = Object.assign({}, pending, changes)
63
+ if (voice) pending.voice = voice
64
+ return true
65
+ }
66
+ if (!current || stopping) return false
67
+ return synth.updateSequence(Object.assign({ sequenceId: current }, changes))
68
+ },
69
+ stop: function() { pending = null; return requestStop() },
70
+ reset: function() {
71
+ pending = null
72
+ current = 0
73
+ stopping = false
74
+ if (listening) {
75
+ synth.removeEventListener('sequenceend', ended)
76
+ listening = false
77
+ }
78
+ },
79
+ id: function() { return current },
80
+ pending: function() { return pending !== null },
81
+ stopping: function() { return stopping }
82
+ }
83
+ }
@@ -0,0 +1,25 @@
1
+ // Compile an application score to the native, millisecond sequence contract.
2
+ // Score envelopes include release; native length marks the release onset.
3
+ module.exports = function sequenceScore(score) {
4
+ const quarters = { '1n':4, '2n':2, '2n.':3, '4n':1, '4n.':1.5, '8n':.5, '8n.':.75, '8t':1/3, '16n':.25, '16t':1/6 }
5
+ function noteFrequency(note) {
6
+ const m = /^(SOL|DO|RE|MI|FA|LA|SI|TI|[A-G])([#b]?)(-?\d+)?$/i.exec(note)
7
+ if (!m) return NaN
8
+ const pitch = {C:0,D:2,E:4,F:5,G:7,A:9,B:11,DO:0,RE:2,MI:4,FA:5,SOL:7,LA:9,SI:11,TI:11}[m[1].toUpperCase()]
9
+ return 440 * Math.pow(2, ((Number(m[3] === undefined ? 4 : m[3])+1)*12+pitch+(m[2]==='#'?1:m[2]==='b'?-1:0)-69)/12)
10
+ }
11
+ const voice = Object.assign({}, score)
12
+ for (const key of ['steps','bpm','swing','stepDuration','noteValue']) delete voice[key]
13
+ return { timeUnit:'ms', loop:false, swing:score.swing || 0, voice,
14
+ steps:score.steps.map(step => {
15
+ const duration = step.duration === undefined
16
+ ? ((step.noteValue || score.noteValue) ? 60000/(score.bpm || 120)*quarters[step.noteValue || score.noteValue] : score.stepDuration || 100)
17
+ : step.duration
18
+ if (step.rest) return {rest:true,duration}
19
+ const overrides = Object.assign({}, step)
20
+ for (const key of ['note','frequency','duration','noteValue']) delete overrides[key]
21
+ const release = step.release === undefined ? score.release === undefined ? Math.min(50,duration-1) : score.release : step.release
22
+ return { frequencies:[step.frequency === undefined ? noteFrequency(step.note) : step.frequency],
23
+ duration, length:Math.max(1,duration-release), overrides }
24
+ }) }
25
+ }
@@ -0,0 +1,174 @@
1
+ > Source snapshot: official TiSynthEngine 1.0.0 documentation at release source commit `e7cbbe8a6f9ab38088fd8029c7b43cef6501101f` (2026-10-24). Upstream `documentation/examples.md` SHA-256: `48104b9d413050d439ee605c92a2456a50b42c4c80e2d7c2c05706eecc7db215`.
2
+
3
+ ## Sustained game motor
4
+
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
+
7
+ ```javascript
8
+ if (!['noteOn', 'noteOff', 'updateVoice'].every(function (name) {
9
+ return typeof synth[name] === 'function'
10
+ })) throw new Error('Held-voice APIs are required')
11
+
12
+ var nextMotorId = 1
13
+ var motorId = null
14
+ var releaseTimers = new Set()
15
+
16
+ function startMotor () {
17
+ if (motorId !== null) return true
18
+ var id = nextMotorId++
19
+ if (!synth.noteOn({ voiceId: id, frequency: 160, waveType: WAVE.NOISE,
20
+ noiseRate: 1500, lowPassHz: 600, attack: 80, release: 120,
21
+ filterType: 'bandpass', filterHz: 220, filterQ: 0.8, volume: 0.15 })) return false
22
+ motorId = id
23
+ return true
24
+ }
25
+
26
+ function changeMotor (boosted) {
27
+ if (motorId === null) return false
28
+ return synth.updateVoice({ voiceId: motorId,
29
+ filterHz: boosted ? 320 : 220, volume: boosted ? 0.18 : 0.15 })
30
+ }
31
+
32
+ function releaseMotorId (id, attempt) {
33
+ if (synth.noteOff({ voiceId: id })) return
34
+ if (attempt >= 2) {
35
+ closeMotorAudio()
36
+ Ti.API.warn('Release queue stayed full; audio shut down to prevent a stuck note')
37
+ return
38
+ }
39
+ var timer = setTimeout(function () {
40
+ releaseTimers.delete(timer)
41
+ releaseMotorId(id, attempt + 1)
42
+ }, 16)
43
+ releaseTimers.add(timer)
44
+ }
45
+
46
+ function stopMotor () {
47
+ if (motorId === null) return
48
+ var id = motorId
49
+ motorId = null
50
+ releaseMotorId(id, 0)
51
+ }
52
+
53
+ function closeMotorAudio () {
54
+ releaseTimers.forEach(function (timer) { clearTimeout(timer) })
55
+ releaseTimers.clear()
56
+ motorId = null
57
+ synth.shutdown()
58
+ }
59
+ ```
60
+
61
+ Wire `startMotor` to acceleration/key down, `changeMotor` to a boost change, and `stopMotor` to release or cancellation. Compatible updates ramp over 10 ms without retriggering: frequency, level, noise clock, low-pass, send and position are live controls. Source/filter type requires a new attack. Start spatial voices with coordinates before editing them; pan updates apply to stereo voices. A stolen ID accepts updates as a no-op; optional priority improves admission without reserving voices.
62
+
63
+ ## Native sequence with chords and edits
64
+
65
+ Start the engine with enough sequence capacity, then store each positive ID. The default interval is a sixteenth note (0.25 beats); rests advance time and gates start release, which can overlap later steps.
66
+
67
+ ```javascript
68
+ synth.setTempo({ bpm: 110 })
69
+ var musicId = synth.playSequence({ swing: 0.12, loop: true, bus: 'music',
70
+ voice: { waveType: WAVE.TRIANGLE, attack: 8, release: 100, volume: 0.3 },
71
+ steps: [{ notes: [60, 64, 67], gate: 0.7 }, { rest: true },
72
+ { notes: [67], gate: 0.5, overrides: { volume: 0.22 } },
73
+ { notes: [64, 67, 72], duration: 0.5, length: 0.75 }]
74
+ })
75
+ if (musicId === 0) throw new Error('Sequence publication rejected')
76
+ synth.setTempo({ bpm: 128 })
77
+ if (!synth.updateSequence({ sequenceId: musicId, swing: 0.18, voice: { volume: 0.25 } })) Ti.API.warn('Edit rejected')
78
+ var positionTimer = setInterval(function () {
79
+ var state = synth.getSequenceState({ sequenceId: musicId })
80
+ if (state) Ti.API.info(JSON.stringify(state))
81
+ }, 200)
82
+ function stopOnlySequence () {
83
+ if (!synth.stopSequence({ sequenceId: musicId })) Ti.API.warn('Stop rejected')
84
+ }
85
+ function closeSequenceAudio () {
86
+ clearInterval(positionTimer)
87
+ synth.shutdown()
88
+ }
89
+ ```
90
+
91
+ The UI timer presents state; native audio schedules every attack/release. For fixed-time cues use `timeUnit: 'ms'`, whose intervals and note lengths survive tempo changes. Edits enter at the next step, swing at the next pair. Up/down/updown modes reorder sounding steps by pitch; normal mode retains rests. A completed/stopped ID is retired; create a new sequence to play again. `sequenceend` identifies the owner, and releases/tails can outlive completion.
92
+
93
+ ## Chorus, delay, reverb and creative saturation
94
+
95
+ This configures the selected share of a running engine; `applyTo` defaults to all categories. Check method presence and Boolean acceptance; partial section updates merge with the last accepted settings. Effects start disabled, input gain starts at 1, and no LFOs are added to voices implicitly.
96
+
97
+ ```javascript
98
+ if (typeof synth.setMasterProcessing !== 'function') {
99
+ throw new Error('Master-processing API is required')
100
+ }
101
+ if (!synth.setMasterProcessing({
102
+ inputGain: 0.7,
103
+ chorus: { enabled: true, rate: 0.6, depth: 0.25, mix: 0.12 },
104
+ delay: { enabled: true, timeMs: 280, sync: 'off', feedback: 0.25, mix: 0.1 },
105
+ reverb: { enabled: true, decay: 1.4, damping: 0.6, mix: 0.14 },
106
+ saturation: { enabled: false }
107
+ })) throw new Error('Processing rejected')
108
+
109
+ // Sync follows the native transport tempo, initially 120 BPM.
110
+ if (!synth.setMasterProcessing({ delay: { sync: 'eighth' } })) {
111
+ Ti.API.warn('Synced delay edit rejected')
112
+ }
113
+
114
+ function setCreativeDrive (enabled) {
115
+ return synth.setMasterProcessing({ saturation: { enabled: enabled, drive: 1.5, mix: 0.2 } })
116
+ }
117
+
118
+ function disableMasterEffects () {
119
+ return synth.setMasterProcessing({ chorus: { enabled: false }, delay: { enabled: false },
120
+ reverb: { enabled: false }, saturation: { enabled: false } })
121
+ }
122
+ ```
123
+
124
+ Call `setCreativeDrive(true)` only for a deliberate timbre change and check acceptance. `disableMasterEffects` closes input and drains bounded tails before exact bypass; it retains input gain 0.7. Set gain back to 1 after tails drain for a unity comparison. Panic stops all sequences, fades voices and invalidates histories. Select music/ambience categories or individual sends when gameplay effects should stay dry.
125
+
126
+ ## Two voice LFOs
127
+
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
+
130
+ ```javascript
131
+ if (typeof synth.noteOn !== 'function' || typeof synth.updateVoice !== 'function') {
132
+ throw new Error('Held-voice APIs are required')
133
+ }
134
+ var modulatedId = 100001 // Reserve an unused ID from the owner's counter in an app.
135
+ if (!synth.noteOn({ voiceId: modulatedId, note: 'A3', waveType: WAVE.SQUARE,
136
+ attack: 100, release: 250, pulseWidth: 0.5, volume: 0.2,
137
+ filterType: 'lowpass', filterHz: 1600, filterQ: 0.8,
138
+ modulations: [
139
+ { wave: 'sine', target: 'filterHz', rate: 0.4, depth: 0.12 },
140
+ { wave: 'triangle', target: 'pulseWidth', sync: 'eighth', depth: 0.15 }
141
+ ] })) throw new Error('Modulated voice rejected')
142
+
143
+ function disableVoiceLfos () {
144
+ return synth.updateVoice({ voiceId: modulatedId, modulations: [] })
145
+ }
146
+ // Call disableVoiceLfos on the UI action and handle false.
147
+ // Later release this ID with the bounded release policy above, or shut down its owner.
148
+ ```
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/chords/patterns do not accept the new array.
151
+
152
+ ## Measure peaks and limiter reduction
153
+
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
+
156
+ ```javascript
157
+ if (!['setStatsEnabled', 'getStats', 'resetStats'].every(function (name) {
158
+ return typeof synth[name] === 'function'
159
+ })) throw new Error('Statistics APIs are required')
160
+ if (!synth.setStatsEnabled(true)) throw new Error('Diagnostics rejected')
161
+ synth.resetStats()
162
+ var mixTimer = setInterval(function () {
163
+ var stats = synth.getStats()
164
+ Ti.API.info(JSON.stringify({ preLimiterDbFS: stats.preLimiterPeakDb,
165
+ maximumReductionDb: stats.maxLimiterReductionDb, latestReductionDb: stats.limiterReductionDb }))
166
+ }, 200)
167
+
168
+ function closeMixMeter () {
169
+ clearInterval(mixTimer)
170
+ if (!synth.setStatsEnabled(false)) Ti.API.warn('Diagnostics disable rejected')
171
+ }
172
+ ```
173
+
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.
@@ -0,0 +1,162 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "date": "2026-10-23",
4
+ "moduleVersion": "1.0.0 beta",
5
+ "status": "corrected-for-tested-Sim80-headphone-case",
6
+ "acceptanceComplete": false,
7
+ "listening": {
8
+ "accepted": true,
9
+ "reviewer": "Cesar",
10
+ "replyTranslation": "Clean, no perceptible distortion.",
11
+ "scenario": "Real Sim80, live synthesis, headphones, eight-output candidate",
12
+ "scope": "Subjective acceptance of this short scene, not every voice/effect or a ten-minute maximum-load pass."
13
+ },
14
+ "implementation": {
15
+ "platform": "Android ARM64",
16
+ "change": "Reuse FIR coefficients across eight consecutive samples per voice while preserving each sample partial-sum and reduction order. Bound tiles at sequence attacks. Keep the previous stereo and other-ABI paths.",
17
+ "gainQualityOrVoiceLimitsChanged": false,
18
+ "outputBufferChanged": false,
19
+ "publicApiChanged": false,
20
+ "diagnosticInterfacesInProduction": false
21
+ },
22
+ "comparisonWithIOS": {
23
+ "sharedBinauralAlgorithm": true,
24
+ "ipadMeasuredRate": 44100,
25
+ "ipadMeasuredBlockFrames": 1024,
26
+ "androidMeasuredRate": 48000,
27
+ "androidMeasuredBlockFrames": 256,
28
+ "ipadTaps": 128,
29
+ "androidTaps": 140,
30
+ "limits": "Different measured hardware/output configurations; larger blocks alone do not cure sustained CPU overload."
31
+ },
32
+ "profilingBeforeCorrection": {
33
+ "route": "speaker",
34
+ "volumeIndex": 80,
35
+ "callbacks": 1801,
36
+ "xruns": 73,
37
+ "cpuBlocksAboveDeadline": 455,
38
+ "withoutOnsetCpuBlocksAboveDeadline": 357,
39
+ "onsetCpuShare": 0.004458489684975288,
40
+ "spatialFirSamples": 1017,
41
+ "audioThreadSamples": 1566,
42
+ "scope": "Actual Sim80, flat250HzCPU sampling with inline symbol attribution, not DSP-only isolation."
43
+ },
44
+ "fourOutputCandidate": {
45
+ "speakerXruns": 1,
46
+ "speakerCpuP95LoadInterval": [
47
+ 1.205,
48
+ 1.21
49
+ ],
50
+ "manualHeadphoneXruns": 37,
51
+ "manualHeadphoneVolumeIndex": 120,
52
+ "manualHeadphoneCpuP95LoadInterval": [
53
+ 1.62,
54
+ 1.625
55
+ ],
56
+ "listening": "Improved but still a couple of perceived distorted moments.",
57
+ "limits": "Speaker profiling and manual headphone capture are distinct runs/routes, not a controlled route comparison."
58
+ },
59
+ "eightOutputCapture": {
60
+ "device": "Android CPH2639",
61
+ "route": "wired headphones",
62
+ "volumeIndex": 120,
63
+ "volumeMax": 160,
64
+ "sampleRate": 48000,
65
+ "callbackFrames": 256,
66
+ "burstFrames": 256,
67
+ "bufferFrames": 512,
68
+ "frames": 480000,
69
+ "capturedSeconds": 10,
70
+ "xruns": 0,
71
+ "routeStayedValid": true,
72
+ "callbacks": 1875,
73
+ "maxVoices": 32,
74
+ "maxTails": 25,
75
+ "wallP95LoadInterval": [
76
+ 1.02,
77
+ 1.025
78
+ ],
79
+ "cpuP95LoadInterval": [
80
+ 0.965,
81
+ 0.97
82
+ ],
83
+ "wallMaxNs": 9150154,
84
+ "cpuMaxNs": 8857462,
85
+ "wallBlocksAboveDeadline": 104,
86
+ "cpuBlocksAboveDeadline": 80,
87
+ "clockErrors": 0,
88
+ "pcmHash": "7f89b839f15ce44d",
89
+ "nonFiniteFrames": 0,
90
+ "peakLeft": 0.8500000238418579,
91
+ "peakRight": 0.8500000238418579,
92
+ "scope": "Private instrumented module with PCM capture/clocks and250Hzsampling. Observed output delivery passed this ten-second scene; callback overruns still prevent a strict render-deadline pass."
93
+ },
94
+ "verification": {
95
+ "streamingVsBlocked": "120000 standalone frames/sixrates8\u2013192k, ramps, wrap and complete state bit-identical on Android; varied short blocks also pass C and forcedAndroidNEON on ARM host.",
96
+ "renderer": "135onset/fullstate,20fractionaltransport/fullstate,9legacy renders, spatial regression and fullheld regression match or pass on ARM host.",
97
+ "review": "Independent source review found no critical/important issue in four/eight windows, clocks, tails, sends, stereo guard or bounded stack.",
98
+ "physicalAcceptance": false,
99
+ "stereoBypassChange": {
100
+ "scope": "One stereo voice, new fix versus immediately preceding205a renderer, same warmed process, both4slots; previous937screen separate",
101
+ "pairs": 10,
102
+ "medianRenderRatio": 1.0113211982131405,
103
+ "medianP95Ratio": 1.0053333333333334,
104
+ "ratios": [
105
+ 1.0005653275429052,
106
+ 1.0126349702109787,
107
+ 1.0196281518419976,
108
+ 1.0126315815643214,
109
+ 1.0206466219701242,
110
+ 1.0100108148619595,
111
+ 0.996436735715484,
112
+ 1.016768104985823,
113
+ 1.0091023055656232,
114
+ 0.9846528665539155
115
+ ],
116
+ "bitExact": true
117
+ },
118
+ "finalProductionClockHeldLegacy": "Byte-identical to the pre-correction renderer after restoring lazy scalar clock reads and forced inlining."
119
+ },
120
+ "productionSourceSha256": {
121
+ "android/jni/SynthOboeEngine.cpp": "cc7f050084871a79cb028b48081a8e51158a3a69f876c59e0f9de1a6ea7ecac6",
122
+ "shared/synth_spatial.h": "d628ef9c60776be463c5dc45a972c9f9bbb0d183656f0dc177211038ba39d75a",
123
+ "tests/spatial_regression.c": "2cf768e4dc18035010819b74837921c8b27ae391670acde0bf56197793c655c0"
124
+ },
125
+ "measuredPrivateArtifact": {
126
+ "apkSha256": "324dc75a4b6caddbf6205915287ad90f68f0c735ac9d395ffb6bfb4821cbd934",
127
+ "aarSha256": "156156df699eabc8647598b5da7fa4f0d20014c3a765b04120a43a0800e40d47",
128
+ "sourceSha256": {
129
+ "android/jni/SynthOboeEngine.cpp": "8b0fc7eb99bc1f0b1d76643776fcb7e8eb540f4ba5cd9379313e7f615670e1b0",
130
+ "shared/synth_spatial.h": "9184a9d81de04c5216c52b96a8860c33bfd3492081089b34df35743d51e90bbb"
131
+ },
132
+ "nativeHashes": {
133
+ "lib/arm64-v8a/liboboe.so": "2696179a9e302f4047ce04146de39f64930bdfaa75d51da63bc7af1a228b8d73",
134
+ "lib/arm64-v8a/libti.synthengine.so": "3c04735e3f4a60b4d5218fadb192121978c30e105f02115383ba68120635c272",
135
+ "lib/armeabi-v7a/liboboe.so": "6eebcff8923f11f79d10bd3404afccab9c8d9ba629709334fc76663894b0e914",
136
+ "lib/armeabi-v7a/libti.synthengine.so": "b07a4b3d2e1d32b71a5267cd47d52024f9f8414d2531867ebcb0281548402919",
137
+ "lib/x86/liboboe.so": "8846bdc73a1ad884e017c7f3082042184bb8c9876651f48e77faef24f7c7eec4",
138
+ "lib/x86/libti.synthengine.so": "b13a3adcc8681090d6c305ca7436d60e367ffbcabce0fec0c64861925fcb28cc",
139
+ "lib/x86_64/liboboe.so": "f1168819d87b8571bb7c8147176854a2e5783bae6e35d2a420d99dc19692db94",
140
+ "lib/x86_64/libti.synthengine.so": "c008b2eb71b38e271a39fc3c5a1d70698e60bd06da578c723637890291970e98"
141
+ }
142
+ },
143
+ "rawEvidenceArchive": "android-sim80-correction-evidence.zip",
144
+ "remaining": [
145
+ "Android ten-minute maximum-load criterion is still open; do not reinterpret this short Sim80 result as that acceptance.",
146
+ "The measured candidate still has80CPU/104wall callbacks above one deadline.",
147
+ "Whole-process192k memory, prior one-voice bypass screen, Android Sim20 renderer elapsed work and strict Sim80 presentation matching retain their prior limits."
148
+ ],
149
+ "publication": "Beta1.0.0, no release or push.",
150
+ "rawEvidenceArchiveSha256": "faff448de458ef0cb5299e234177f17a914f7801c27581ab969a9010a68c3c5f",
151
+ "productionAndroidBuild": {
152
+ "aarSha256": "7e98bf4fbc6fbe4d1f4a5d9f5a561137522e831fb54480ae0310dd22b60a0c2c",
153
+ "nativeSoSha256": {
154
+ "arm64-v8a": "631aaca4f4dabcedc33cf3bf1b23601de3da6291448a299e195cbf5b07f526e3",
155
+ "armeabi-v7a": "1a4100a25f920784dfc5bda91f30fa6f405e2b85deee51a8b14ce5502a2cf47c",
156
+ "x86": "8f4545e24cb7f35220445b6e8c04f561bbf5a6cbf55b755c8e8aeffe923e748d",
157
+ "x86_64": "96a8c5224129de2cd215c9d3b6a2cc50c08865f2de35bf95274026ea452a4a57"
158
+ },
159
+ "realFourAbiBuild": true,
160
+ "diagnosticCodeAbsent": true
161
+ }
162
+ }