@denaudio/den 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -0
- package/LICENSE-APACHE +202 -0
- package/LICENSE-MIT +21 -0
- package/README.md +107 -2
- package/dist/biquad-eq.d.ts +11 -0
- package/dist/biquad-eq.js +35 -0
- package/dist/catalog-filter-math.d.ts +4 -0
- package/dist/catalog-filter-math.js +30 -0
- package/dist/character-filter.d.ts +13 -0
- package/dist/character-filter.js +53 -0
- package/dist/convolution-spectrum.d.ts +25 -0
- package/dist/convolution-spectrum.js +82 -0
- package/dist/convolution.d.ts +17 -0
- package/dist/convolution.js +87 -0
- package/dist/crossover.d.ts +11 -0
- package/dist/crossover.js +17 -0
- package/dist/curve-shaper.d.ts +26 -0
- package/dist/curve-shaper.js +73 -0
- package/dist/delay-fx.d.ts +37 -0
- package/dist/delay-fx.js +84 -0
- package/dist/delay-readhead.d.ts +19 -0
- package/dist/delay-readhead.js +54 -0
- package/dist/delay-settings.d.ts +55 -0
- package/dist/delay-settings.js +20 -0
- package/dist/drive.d.ts +24 -0
- package/dist/drive.js +116 -0
- package/dist/dual-head-delay.d.ts +28 -0
- package/dist/dual-head-delay.js +77 -0
- package/dist/dynamics.d.ts +61 -0
- package/dist/dynamics.js +130 -0
- package/dist/envelope.d.ts +34 -0
- package/dist/envelope.js +62 -0
- package/dist/filter.d.ts +9 -0
- package/dist/filter.js +34 -0
- package/dist/filterbank-vocoder.d.ts +35 -0
- package/dist/filterbank-vocoder.js +52 -0
- package/dist/formant-bank.d.ts +15 -0
- package/dist/formant-bank.js +28 -0
- package/dist/freeze-reverb.d.ts +35 -0
- package/dist/freeze-reverb.js +61 -0
- package/dist/frequency-shifter.d.ts +24 -0
- package/dist/frequency-shifter.js +57 -0
- package/dist/gate.d.ts +2 -0
- package/dist/gate.js +12 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +12 -0
- package/dist/instrument-example.d.ts +12 -0
- package/dist/instrument-example.js +29 -0
- package/dist/instrument-settings.d.ts +95 -0
- package/dist/instrument-settings.js +36 -0
- package/dist/instrument.d.ts +42 -0
- package/dist/instrument.js +166 -0
- package/dist/lfo.d.ts +19 -0
- package/dist/lfo.js +55 -0
- package/dist/live-buffer.d.ts +39 -0
- package/dist/live-buffer.js +52 -0
- package/dist/live-granular.d.ts +41 -0
- package/dist/live-granular.js +64 -0
- package/dist/lookahead-limiter.d.ts +31 -0
- package/dist/lookahead-limiter.js +67 -0
- package/dist/loop-crossfade.d.ts +28 -0
- package/dist/loop-crossfade.js +72 -0
- package/dist/modulation-fx.d.ts +47 -0
- package/dist/modulation-fx.js +58 -0
- package/dist/modulation.d.ts +132 -0
- package/dist/modulation.js +228 -0
- package/dist/mpe-expression.d.ts +36 -0
- package/dist/mpe-expression.js +73 -0
- package/dist/multiband-dynamics.d.ts +50 -0
- package/dist/multiband-dynamics.js +60 -0
- package/dist/musical-controls.d.ts +76 -0
- package/dist/musical-controls.js +125 -0
- package/dist/musical-examples.d.ts +21 -0
- package/dist/musical-examples.js +142 -0
- package/dist/musical-materials.d.ts +110 -0
- package/dist/musical-materials.js +56 -0
- package/dist/oscillator.d.ts +10 -0
- package/dist/oscillator.js +35 -0
- package/dist/oversampled-drive.d.ts +19 -0
- package/dist/oversampled-drive.js +99 -0
- package/dist/performance.d.ts +61 -0
- package/dist/performance.js +241 -0
- package/dist/pitch-quantizer.d.ts +26 -0
- package/dist/pitch-quantizer.js +88 -0
- package/dist/prepared-convolution.d.ts +26 -0
- package/dist/prepared-convolution.js +104 -0
- package/dist/resident-recorder.d.ts +28 -0
- package/dist/resident-recorder.js +68 -0
- package/dist/resident-time-stretch.d.ts +46 -0
- package/dist/resident-time-stretch.js +136 -0
- package/dist/resonator.d.ts +32 -0
- package/dist/resonator.js +63 -0
- package/dist/reverb.d.ts +27 -0
- package/dist/reverb.js +52 -0
- package/dist/sample.d.ts +113 -0
- package/dist/sample.js +212 -0
- package/dist/source.d.ts +58 -0
- package/dist/source.js +113 -0
- package/dist/spatial-chains.d.ts +50 -0
- package/dist/spatial-chains.js +76 -0
- package/dist/spectral-fft.d.ts +27 -0
- package/dist/spectral-fft.js +84 -0
- package/dist/spectral-freeze.d.ts +24 -0
- package/dist/spectral-freeze.js +74 -0
- package/dist/spectral-gate.d.ts +23 -0
- package/dist/spectral-gate.js +58 -0
- package/dist/spectral-stft.d.ts +14 -0
- package/dist/spectral-stft.js +70 -0
- package/dist/spectral-texture.d.ts +46 -0
- package/dist/spectral-texture.js +141 -0
- package/dist/spectral.d.ts +18 -0
- package/dist/spectral.js +10 -0
- package/dist/state-variable-filter.d.ts +17 -0
- package/dist/state-variable-filter.js +25 -0
- package/dist/stereo-delay.d.ts +45 -0
- package/dist/stereo-delay.js +70 -0
- package/dist/virtual-analog.d.ts +26 -0
- package/dist/virtual-analog.js +66 -0
- package/dist/voice-policy.d.ts +37 -0
- package/dist/voice-policy.js +245 -0
- package/dist/wavetable-bands.d.ts +37 -0
- package/dist/wavetable-bands.js +145 -0
- package/dist/wavetable-read.d.ts +6 -0
- package/dist/wavetable-read.js +11 -0
- package/dist/wavetable.d.ts +29 -0
- package/dist/wavetable.js +49 -0
- package/dist/windowed-pitch-shift.d.ts +34 -0
- package/dist/windowed-pitch-shift.js +51 -0
- package/docs/catalog-audition.md +76 -0
- package/docs/catalog-filters.md +125 -0
- package/docs/catalog-modulation-fx.md +220 -0
- package/docs/catalog-status.md +433 -0
- package/docs/character-filter.md +139 -0
- package/docs/contracts.md +141 -0
- package/docs/control-modulation.md +194 -0
- package/docs/convolution.md +50 -0
- package/docs/curve-shaper.md +117 -0
- package/docs/delay-fx-capture-findings.json +134 -0
- package/docs/delay-fx.md +202 -0
- package/docs/delay-readhead.md +121 -0
- package/docs/delay-settings-candidate.json +333 -0
- package/docs/delay-settings.md +111 -0
- package/docs/dependency-findings.md +58 -0
- package/docs/deployment-status.md +37 -0
- package/docs/drive.md +152 -0
- package/docs/dual-head-delay.md +131 -0
- package/docs/dynamics.md +180 -0
- package/docs/envelope-integer-staging.md +64 -0
- package/docs/filter-materialization-results.json +71 -0
- package/docs/filter-materialization.md +135 -0
- package/docs/filter-oscillator.md +126 -0
- package/docs/filterbank-vocoder-entry.md +58 -0
- package/docs/freeze-reverb.md +179 -0
- package/docs/frequency-shifter.md +133 -0
- package/docs/frontend-audition.md +22 -0
- package/docs/gain-schedule-verification.md +53 -0
- package/docs/initial-acceptance.md +79 -0
- package/docs/instrument-performance.md +191 -0
- package/docs/instrument-settings.md +130 -0
- package/docs/instrument.md +204 -0
- package/docs/integration-candidate.md +114 -0
- package/docs/live-buffer.md +163 -0
- package/docs/live-granular.md +179 -0
- package/docs/lookahead-multiband.md +183 -0
- package/docs/loop-crossfade.md +99 -0
- package/docs/mpe-expression.md +65 -0
- package/docs/musical-clock-seek-boundary.md +31 -0
- package/docs/musical-controls-contract.md +144 -0
- package/docs/musical-examples.md +255 -0
- package/docs/oversampled-drive.md +153 -0
- package/docs/performance.md +53 -0
- package/docs/pitch-quantizer.md +55 -0
- package/docs/prepared-convolution.md +197 -0
- package/docs/realtime-audition.md +63 -0
- package/docs/resident-recorder.md +151 -0
- package/docs/resident-time-stretch.md +196 -0
- package/docs/sample.md +191 -0
- package/docs/sound-audition-headroom.md +103 -0
- package/docs/sources.md +81 -0
- package/docs/spatial-chains-entry.md +251 -0
- package/docs/spectral-freeze-entry.md +132 -0
- package/docs/spectral-gate-entry.md +134 -0
- package/docs/spectral-large-candidate.md +81 -0
- package/docs/spectral-texture-entry.md +178 -0
- package/docs/spectral.md +127 -0
- package/docs/virtual-analog.md +77 -0
- package/docs/voice-policy.md +38 -0
- package/docs/wavetable-bands.md +138 -0
- package/docs/wavetable.md +101 -0
- package/docs/windowed-pitch-shift.md +112 -0
- package/package.json +269 -4
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Instrument performance investigation
|
|
2
|
+
|
|
3
|
+
Correctness follow-up: the former oscillator/filter and amplitude caches also
|
|
4
|
+
flushed recoverable tiny values, independently of the depth-only optimization.
|
|
5
|
+
Those three paths now compose returned Nodes directly. A failing-before/passing-
|
|
6
|
+
after regression checks tiny oscillator → large custom-filter gain, large
|
|
7
|
+
oscillator → tiny amplitude envelope, tiny custom-filter output, smallest f32
|
|
8
|
+
subnormal and negative-zero sign recovery. Private DSL internals are not used.
|
|
9
|
+
Compilation remains possible at 1/4 voices; WASM changes from 804852/1050090 to
|
|
10
|
+
818400/1104282 bytes. Measurements below predate this correctness correction
|
|
11
|
+
unless explicitly labeled otherwise, and are retained as historical evidence.
|
|
12
|
+
The corrected composition passes 124 numerical/state tests and five packed
|
|
13
|
+
consumers. Its local packed four-voice capture has hot wall/CPU medians 445/436
|
|
14
|
+
µs, 26 wall and 32 CPU threshold exceedances, zero frame gaps and raw/release
|
|
15
|
+
residuals 6.140e-9/8.892e-9. This is a correctness fix with observed performance
|
|
16
|
+
cost, not an optimization or a portable real-time guarantee. Underrun/overrun
|
|
17
|
+
counts require separating CPU from non-CPU intervals and observer/host effects;
|
|
18
|
+
they do not alone identify a product defect.
|
|
19
|
+
|
|
20
|
+
Status: incomplete. Numerical continuity is not a real-time readiness verdict.
|
|
21
|
+
Baseline source is `9a75f9c07e89d0dd77d598bf3e35ee51af1eb26e`, unworklet
|
|
22
|
+
0.4.1, Chromium 151.0.7922.173, 48000 Hz and 128-frame quanta. All audio is
|
|
23
|
+
CANDIDATE. No hardware loopback or listening approval is claimed.
|
|
24
|
+
|
|
25
|
+
Manual reproductions are in `tests/instrument-performance/`. Each invocation
|
|
26
|
+
creates a separate directory under `artifacts/instrument-investigation/` with
|
|
27
|
+
source copies/hashes, WASM, raw samples, per-block timings and a manifest.
|
|
28
|
+
Preserve all trials, outliers and failed preflights; do not retry in place.
|
|
29
|
+
|
|
30
|
+
## Cost controls
|
|
31
|
+
|
|
32
|
+
The direct Node 24 trials retain wall time and **process-wide** CPU, which is
|
|
33
|
+
not render-thread CPU. Chrome trace `dur` and `tdur` measure the enclosing
|
|
34
|
+
Render event, including graph/observer work. They are not instrument-only cost.
|
|
35
|
+
The nominal quantum budget is 2666.667 microseconds.
|
|
36
|
+
|
|
37
|
+
| Configuration | Node median, microseconds | Chrome median, microseconds |
|
|
38
|
+
| --- | ---: | ---: |
|
|
39
|
+
| 1 voice capacity | 228 | 110–113 |
|
|
40
|
+
| 4 voice capacity, four sounding | 1047–1049 | 351–357 |
|
|
41
|
+
| 4 voice capacity, none sounding | 1064–1072 | 352–364 |
|
|
42
|
+
| 16 voice capacity | 33053–33602 | 1507–1521 |
|
|
43
|
+
|
|
44
|
+
These are construction capacities, not product limits. Inactive slots currently
|
|
45
|
+
execute DSP. The dynamic voice-policy-only control costs approximately 6, 7 and
|
|
46
|
+
13 microseconds at capacities 4, 8 and 16; it does not explain whole-engine cost.
|
|
47
|
+
Node and Chrome differ substantially. Yielding between blocks did not resolve
|
|
48
|
+
Node's 16-voice cost, so tier installation is not an established explanation.
|
|
49
|
+
|
|
50
|
+
Materializing the instrument's combined modulation depths and final stereo mix
|
|
51
|
+
reduces WASM from 1083626 to 1049832 bytes and Node median from roughly
|
|
52
|
+
1015–1063 to 570–659 microseconds. Three captured scenarios are bit-identical,
|
|
53
|
+
including modulation, sample-varying cutoff/Q and release. Chrome medians remain
|
|
54
|
+
359–366 microseconds versus baseline 358–360; this is **not** a demonstrated
|
|
55
|
+
browser deadline fix. The candidate remains detached from production source.
|
|
56
|
+
|
|
57
|
+
## Frame metadata counterexample
|
|
58
|
+
|
|
59
|
+
Run `2026-10-04T13-37-51.511Z-browser`, trial `trial0-four-active4-poll1`,
|
|
60
|
+
reproduces currentFrame values 131840, 131840, 132096. Entry and exit values
|
|
61
|
+
agree within each callback; currentTime repeats too. All input blocks contain
|
|
62
|
+
128 samples. The complete raw stream matches the independent 440 Hz sinusoid
|
|
63
|
+
with maximum residual 6.1391e-9.
|
|
64
|
+
|
|
65
|
+
At anomalous quantum 820, normal continuation has maximum residual 5.4479e-9.
|
|
66
|
+
An independently predicted repeated or skipped 128-frame block instead differs
|
|
67
|
+
by 0.06946. This reproduction shows a frame-clock metadata anomaly without
|
|
68
|
+
duplicated/skipped audio in the captured graph. It does not establish hardware
|
|
69
|
+
output continuity, or retroactively explain the old CI failure whose raw data
|
|
70
|
+
was not retained. CI used Chromium 148; this reproduction uses Chromium 151.
|
|
71
|
+
|
|
72
|
+
The packed test now writes raw/metrics/manifest before asserting frame gaps.
|
|
73
|
+
The CI integration owner should also upload failure artifacts (`always()`),
|
|
74
|
+
because success-only upload loses the evidence needed to diagnose failures.
|
|
75
|
+
|
|
76
|
+
## Underrun controls without trace or observation worklet
|
|
77
|
+
|
|
78
|
+
Run `2026-10-04T13-48-23.859Z-browser` uses native Analyser capture of the final
|
|
79
|
+
32768 samples only. This is not full-stream continuity evidence. Chrome tracing
|
|
80
|
+
and the capture AudioWorklet are both absent. Native playbackStats counters are
|
|
81
|
+
sampled at startup, after one audio second, and at the end; differences below
|
|
82
|
+
exclude startup. All three trials per condition are retained.
|
|
83
|
+
|
|
84
|
+
| Condition | Steady underrun events, trials 0 / 1 / 2 | Duration, seconds |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| Native AudioBufferSource | 0 / 0 / 0 | 0 / 0 / 0 |
|
|
87
|
+
| Four-voice instrument | 42 / 0 / 81 | 0.42084 / 0 / 0.81162 |
|
|
88
|
+
|
|
89
|
+
These browser-internal counters are not hardware-loopback measurements. They
|
|
90
|
+
nevertheless prevent dismissing all observed failures as tracing or capture-AW
|
|
91
|
+
overhead. No-trace trials have no per-quantum duration measurement; an empty
|
|
92
|
+
trace must not be interpreted as zero deadline overruns. Aggregate CDP process
|
|
93
|
+
CPU is retained separately and includes initialization and other browser work.
|
|
94
|
+
|
|
95
|
+
Further work must correlate full raw capture, frame metadata, playbackStats and
|
|
96
|
+
trace/no-trace conditions, then validate any instrument change at the same head.
|
|
97
|
+
Shared filter changes are outside this investigation's production ownership.
|
|
98
|
+
|
|
99
|
+
Full raw, no-trace follow-up `2026-10-04T13-50-17.498Z-browser` retains one trial
|
|
100
|
+
of each setting: native without timestamp polling, native with polling, four-slot
|
|
101
|
+
instrument silent, and four-slot instrument sounding. Post-warmup underrun
|
|
102
|
+
counts are respectively 12, 2, 101 and 61; durations are 0.12024, 0.02004,
|
|
103
|
+
1.01202 and 0.61122 seconds. Native sample counters have zero breaks. Sounding
|
|
104
|
+
instrument PCM residual is 6.145e-9. The silent instrument has two frame metadata
|
|
105
|
+
anomalies; silence cannot establish whether audio frames were lost. These controls
|
|
106
|
+
show both observation overhead and instrument-associated failures. Full raw graph
|
|
107
|
+
continuity and browser output underrun counters describe different boundaries.
|
|
108
|
+
|
|
109
|
+
## Instrument-only depth materialization
|
|
110
|
+
|
|
111
|
+
`2026-10-04T13-56-04.747Z-owned` compares the original source, depth-only
|
|
112
|
+
materialization, and the earlier depth-plus-final-mix candidate. The latter is
|
|
113
|
+
rejected: a valid `gain=1e-35` output becomes zero at the new state write. The
|
|
114
|
+
regression checks its independently expected nonzero value and downstream gain.
|
|
115
|
+
The upstream f32/f64 state and buffer stores flush magnitudes below 1e-30;
|
|
116
|
+
audio output stores do not. No upstream source was changed.
|
|
117
|
+
|
|
118
|
+
Only combined pitch/cutoff exponents are cached in the selected change. Such
|
|
119
|
+
exponents below 1e-30 already produce an exactly-one rounded octave ratio in
|
|
120
|
+
the existing polynomial. The stored expression is already f32, so other finite
|
|
121
|
+
values gain no extra rounding. Final mix remains an expression. Cache entries
|
|
122
|
+
are overwritten before every read, including reset and restoration.
|
|
123
|
+
|
|
124
|
+
All 42 combinations of 1/4 voices, 44100/48000/96000 Hz and seven scenarios
|
|
125
|
+
(normal, dynamic, reset, cold-node restore, custom parts, tiny output, tiny
|
|
126
|
+
depth) are bit-identical for the depth-only variant, with zero scrubbed samples.
|
|
127
|
+
The six tiny-output comparisons reject the final-mix variant. Raw PCM and
|
|
128
|
+
source copies are preserved for each variant, not just passing cases.
|
|
129
|
+
|
|
130
|
+
| Capacity | Variant | WASM bytes | First process, ms | Hot median, µs |
|
|
131
|
+
| --- | --- | ---: | ---: | ---: |
|
|
132
|
+
| 1 | baseline | 813236 | 66.28 | 231.20 |
|
|
133
|
+
| 1 | depth cache | 804852 | 60.43 | 151.61 |
|
|
134
|
+
| 4 | baseline | 1083626 | 120.00 | 1203.85 |
|
|
135
|
+
| 4 | depth cache | 1050090 | 124.10 | 676.40 |
|
|
136
|
+
|
|
137
|
+
These are single Node timing trials (2500 quanta, hot after 500), with inactive
|
|
138
|
+
voices, held capacity 128 and zero-filled native driver parameter buffers. Every block's wall/process
|
|
139
|
+
CPU is retained. Compile times are 860/858 ms for 1 voice and 962/1012 ms for
|
|
140
|
+
4 voices; instantiate times are 4.21/3.24 and 5.89/5.56 ms. The first process
|
|
141
|
+
includes lazy compilation. No uniform cold-time improvement is established.
|
|
142
|
+
|
|
143
|
+
Chrome no-trace comparison `2026-10-04T13-58-07.908Z-browser` uses two full-raw
|
|
144
|
+
12-second trials per variant/capacity, all with held capacity 128. All raw
|
|
145
|
+
sinusoid checks pass and frame metadata gaps are zero. The source snapshots
|
|
146
|
+
contain the exact detached baseline/candidate; production changes during the
|
|
147
|
+
run do not affect those compiled variants.
|
|
148
|
+
|
|
149
|
+
| Capacity / variant | createNode, ms (two trials) | Underrun events | Underrun seconds |
|
|
150
|
+
| --- | --- | --- | --- |
|
|
151
|
+
| 1 baseline | 43.49 / 42.38 | 17 / 25 | 0.17034 / 0.25050 |
|
|
152
|
+
| 1 depth cache | 35.48 / 37.69 | 20 / 40 | 0.20040 / 0.40080 |
|
|
153
|
+
| 4 baseline | 53.42 / 63.84 | 53 / 102 | 0.53106 / 1.02204 |
|
|
154
|
+
| 4 depth cache | 43.96 / 40.62 | 69 / 71 | 0.69138 / 0.71142 |
|
|
155
|
+
|
|
156
|
+
Average reported latency is 31.29 ms in all eight trials. createNode includes
|
|
157
|
+
module/node setup, not the first audio process. Its reduction is not proof of a
|
|
158
|
+
deadline improvement. These trials do **not** demonstrate an underrun fix.
|
|
159
|
+
Although counters are subtracted at the fixture's warm marker, playbackStats
|
|
160
|
+
`totalDuration` is only 0.002666 seconds there. Unlike the unobserved controls,
|
|
161
|
+
these full-capture deltas can include startup and must not be labeled strictly
|
|
162
|
+
steady-state counts. Trace-based per-quantum hot measurements remain separate.
|
|
163
|
+
|
|
164
|
+
Trace follow-up `2026-10-04T14-01-35.795Z-browser` (one trial each) gives:
|
|
165
|
+
|
|
166
|
+
| Capacity / variant | Hot wall median / p99, µs | Wall overruns | CPU overruns | playbackStats underruns |
|
|
167
|
+
| --- | --- | ---: | ---: | ---: |
|
|
168
|
+
| 1 baseline | 111 / 898 | 7 | 8 | 16 |
|
|
169
|
+
| 1 depth cache | 109 / 537 | 3 | 3 | 41 |
|
|
170
|
+
| 4 baseline | 354 / 2189 | 23 | 24 | 53 |
|
|
171
|
+
| 4 depth cache | 356 / 1910 | 13 | 14 | 165 |
|
|
172
|
+
|
|
173
|
+
All raw continuity checks pass; frame gaps are zero. The four-voice candidate
|
|
174
|
+
still has a 39288 µs maximum hot Render event. Median CPU is 103/101 µs for
|
|
175
|
+
one voice and 346/347 µs for four. Neither the smaller tail count in this single
|
|
176
|
+
trace nor the successful PCM checks justify real-time readiness. Browser median
|
|
177
|
+
cost is unchanged and underruns persist. The change is a Node execution and
|
|
178
|
+
generated-size improvement, not a demonstrated browser performance fix.
|
|
179
|
+
|
|
180
|
+
Raw-preserving Node repeat `2026-10-04T14-03-30.314Z-owned-timing` records
|
|
181
|
+
2500 inactive quanta per variant, PCM and every CPU/wall sample. Hot medians
|
|
182
|
+
are 245/152 µs (one voice) and 1129/617 µs (four), baseline/cache. First-block
|
|
183
|
+
times are 79/91 and 137/112 ms. The original timing-only subtrial retained
|
|
184
|
+
timings but no PCM; it remains preserved as such rather than relabeled complete.
|
|
185
|
+
|
|
186
|
+
To reproduce the selected comparison, run `node tests/instrument-performance/owned.mjs`,
|
|
187
|
+
then supply its evidence directory as `PROPOSALS_DIR` to `benchmark-owned.mjs`
|
|
188
|
+
or to `browser.mjs` with `CONTROL_MODE=owned`. `NO_TRACE=1` separates playback
|
|
189
|
+
counters from tracing, `TRIALS` controls repetitions, and `CHROMIUM_PATH` selects
|
|
190
|
+
the browser executable. Original artifacts and source snapshots are immutable;
|
|
191
|
+
new invocations produce new timestamped directories.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Bass / Percussion / Pad candidates (GEN-619)
|
|
2
|
+
|
|
3
|
+
These are three editable settings of `createInstrument`, using the existing
|
|
4
|
+
stock oscillator, filter, envelopes, LFO and voice policy. Every setting and
|
|
5
|
+
render is **CANDIDATE**. There is no approved golden or physical listening
|
|
6
|
+
acceptance. Runtime remains **NOT_CLEARED**; earlier frame gaps and underruns
|
|
7
|
+
recorded in [PR #26](https://github.com/yuichkun/den/pull/26) are not erased by a
|
|
8
|
+
successful run here. This change is stacked on that unmerged integration
|
|
9
|
+
candidate at `a1a6fd768f7f350f1072babefe00859f1330bfe6`, which includes the
|
|
10
|
+
unmerged instrument composition from PR #22 and main's envelope staging fix.
|
|
11
|
+
|
|
12
|
+
## Use the existing APIs
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// processor.ts, compiled by @unworklet/unplugin as usual
|
|
16
|
+
import { createInstrument, bassConfig } from '@denaudio/den/instrument';
|
|
17
|
+
export const processor = createInstrument(bassConfig);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// Browser host, after the normal user-gesture audio startup
|
|
22
|
+
import { createNode } from '@unworklet/core';
|
|
23
|
+
import { bassParameters } from '@denaudio/den/instrument';
|
|
24
|
+
import processor from './processor.ts?worklet';
|
|
25
|
+
const node = await createNode(context, processor, { initial: bassParameters });
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`percussionConfig` / `percussionParameters` and `padConfig` / `padParameters`
|
|
29
|
+
work the same way. All 22 native parameters are explicitly present in each
|
|
30
|
+
object; the complete values live in `src/instrument-settings.ts`. Edit those
|
|
31
|
+
ordinary values or the existing node AudioParams. There is no new preset,
|
|
32
|
+
serialization, MIDI, routing or loader framework, no new export-map entry and
|
|
33
|
+
no changed engine defaults. Browser compilation remains 48 kHz only on
|
|
34
|
+
unworklet 0.4.1; 44.1/96 kHz coverage is offline.
|
|
35
|
+
|
|
36
|
+
- Bass: one saw voice, last-held mono legato, gain .12, 5 ms amplitude attack,
|
|
37
|
+
120 ms decay/release, .7 sustain. The filter rises briefly from 300 to 2400 Hz
|
|
38
|
+
then settles near 455 Hz. Overlapping held notes preserve the transient;
|
|
39
|
+
this does not implement portamento.
|
|
40
|
+
- Percussion: one retriggering sine voice, gain .16, 1 ms amplitude attack,
|
|
41
|
+
220 ms decay to zero, 40 ms release. Pitch falls two octaves over 60 ms;
|
|
42
|
+
note 36 is about 261.6 Hz at onset, settling to 65.4 Hz. Zero attack enters
|
|
43
|
+
the first decay step, so the first pitch is slightly below that peak. It is
|
|
44
|
+
tonal kick/tom-like percussion, with no noise generator.
|
|
45
|
+
- Pad: four saw voices, gain .045, 800 ms attack, 1.2 s decay to .7 sustain,
|
|
46
|
+
2.4 s release. The shared .12 Hz LFO has an 8.333 s cycle, ±3-cent pitch
|
|
47
|
+
depth, .08 tremolo depth and .2-octave filter depth. The dry output is dual
|
|
48
|
+
mono; no stereo spread or unison is claimed.
|
|
49
|
+
|
|
50
|
+
All envelopes are linear. Release and retrigger begin at the previously emitted
|
|
51
|
+
level. Hard reset/retrigger/stealing can be discontinuous; this is not a
|
|
52
|
+
click-free claim. Sustain zero does not free a held MIDI identity. Send the
|
|
53
|
+
balanced note-offs supplied by the fixtures. There is no CC64 sustain.
|
|
54
|
+
|
|
55
|
+
Use native `snapshot()` / `restore()` for the **same processor/schema/rate**.
|
|
56
|
+
Construction changes are not portable preset migration. Restoring a new node
|
|
57
|
+
must not revive held notes. For a cold in-place restore, restore, emit `reset`,
|
|
58
|
+
render at least one quantum, then send fresh notes. Reset and MIDI in the same
|
|
59
|
+
quantum follow the reset-wins rule.
|
|
60
|
+
|
|
61
|
+
## Reproduction and evidence
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
npm ci
|
|
65
|
+
npm run check
|
|
66
|
+
npm test
|
|
67
|
+
# Focused packed candidate check with an installed Chromium:
|
|
68
|
+
CHROMIUM_PATH=/usr/bin/chromium node --test tests/sound-candidates-packed.test.mjs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The regular test command discovers both `tests/instrument-settings.spec.ts`
|
|
72
|
+
and `tests/sound-candidates-packed.test.mjs`. The latter packs den into a clean
|
|
73
|
+
locked consumer, checks the public import and TypeScript declarations, compiles
|
|
74
|
+
all three processors through Vite/unworklet, and runs the muted real Chromium
|
|
75
|
+
MIDI, parameter, release/reset/bypass and snapshot probes at 48 kHz. It does
|
|
76
|
+
not publish a site or change the integration preview.
|
|
77
|
+
|
|
78
|
+
`tests/sound-candidates-consumer/fixtures.mjs` fixes the FIFO MIDI inputs. The
|
|
79
|
+
Bass and Percussion phrases are 262144 frames at 48 kHz. Pad is 768000 frames
|
|
80
|
+
(16 seconds), with note-offs at 8, 9, 10 and 11 seconds. The last 2.6 seconds
|
|
81
|
+
cover silence following the final release. Lower and higher offline rates
|
|
82
|
+
preserve elapsed time by rounding each event to the nearest corresponding
|
|
83
|
+
128-frame quantum; expanded timestamps and frame lengths are retained.
|
|
84
|
+
|
|
85
|
+
Separate maximum-velocity inputs cover Bass legato and Percussion release
|
|
86
|
+
retrigger; Pad's maximum case uses four coherent A4 notes. Pad also has a
|
|
87
|
+
released-slot reuse / oldest-held steal case and an obsolete note-off that
|
|
88
|
+
must not release the replacement. These stress renders remain separate from
|
|
89
|
+
the three listening phrases.
|
|
90
|
+
|
|
91
|
+
Each run writes a new `artifacts/sound-candidates/<timestamp>/` directory,
|
|
92
|
+
preserving previous failures. Raw stereo float WAVs are unnormalized and dry;
|
|
93
|
+
static min/max waveforms have fixed ±1 scale. The manifest binds the source
|
|
94
|
+
commit/dirty bit and SHA-256 source hashes, tarball/lock hashes, dependency
|
|
95
|
+
versions, WASM size/hashes, verification results and candidate status. The
|
|
96
|
+
render evidence binds every construction option, parameter, expanded MIDI
|
|
97
|
+
array, rate, channels, seed (null), frame length, PCM/WAV hashes, exact-silence
|
|
98
|
+
boundary and peak/RMS/headroom/clipped/nonfinite measurements. Phrase renders
|
|
99
|
+
are repeated and their full PCM hashes must agree. Full-render and named-window
|
|
100
|
+
measurements are both retained.
|
|
101
|
+
|
|
102
|
+
Headroom is measured for these settings and inputs. The full-scale gate rejects
|
|
103
|
+
peak ≥1, nonfinite PCM, or scrubbed samples; it is also tested against deliberate
|
|
104
|
+
wrong signals. **The integration diagnostic's .8 analytic bound does not apply
|
|
105
|
+
to these saw/modulated settings.** No limiter or hidden normalization is added.
|
|
106
|
+
Arbitrary edits, resonances, pitches or voice counts are not covered by a
|
|
107
|
+
measurement for these fixed fixtures.
|
|
108
|
+
|
|
109
|
+
The independent numerical tests use literal candidate values, closed-form
|
|
110
|
+
linear envelope timing and replacement DC/frequency/cutoff probes. Existing
|
|
111
|
+
sine/static-biquad, saw-property, voice-policy and wrong-answer tests remain in
|
|
112
|
+
place. An audio-rate direct-form biquad is not treated as an exact dynamic SVF
|
|
113
|
+
reference. Headless signal checks are not device playback or listening approval.
|
|
114
|
+
|
|
115
|
+
## Listening and promotion boundary
|
|
116
|
+
|
|
117
|
+
Review the three canonical 48 kHz `*-phrase-48000.wav` files together, at a
|
|
118
|
+
comfortable user-controlled playback level:
|
|
119
|
+
|
|
120
|
+
1. Bass: low core and upper-register audibility; brief bright transient;
|
|
121
|
+
held-note transition/return without restarting that transient.
|
|
122
|
+
2. Percussion: purposeful short pitch drop, velocity contrast, ringing/chirp
|
|
123
|
+
quality; the burst intentionally truncates decay and retriggers in release.
|
|
124
|
+
3. Pad: smooth growth, clear chord and subtle slow motion; staggered independent
|
|
125
|
+
long releases, with voice stealing assessed separately.
|
|
126
|
+
|
|
127
|
+
Any sound-affecting change requires regenerating the raw audio, waveform and
|
|
128
|
+
manifest. Only explicit human approval tied to those hashes, followed by a
|
|
129
|
+
separate reviewed change, can promote a candidate to a golden. This initial
|
|
130
|
+
change does not merge the integration candidate or clear its runtime gate.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# MIDI instrument composition
|
|
2
|
+
|
|
3
|
+
`src/instrument.ts` composes the existing voice policy, oscillator, filter,
|
|
4
|
+
Envelope and LFO. `createInstrument(config)` returns an unworklet processor;
|
|
5
|
+
`instrument` is the diagnostic polyphonic default. There is no new MIDI,
|
|
6
|
+
parameter, routing, snapshot, loader or test framework.
|
|
7
|
+
|
|
8
|
+
The initial sound is **CANDIDATE**, not an approved golden or one of the eventual
|
|
9
|
+
bass/percussion/pad presets. Those sounds will use this same engine.
|
|
10
|
+
|
|
11
|
+
## Construction and replacement
|
|
12
|
+
|
|
13
|
+
Pass a required configuration to `createInstrument`:
|
|
14
|
+
|
|
15
|
+
- `mode`: `mono` or `poly`; `legato`, `capacity`, `heldCapacity` follow the voice
|
|
16
|
+
policy. Mono uses one voice; poly defaults to 4. Engine capacity is currently
|
|
17
|
+
limited to 16, held capacity to the underlying policy's 256. These are fixed
|
|
18
|
+
construction/compilation bounds, not approved product-wide polyphony limits.
|
|
19
|
+
- `waveform`: `sine` (default) or `saw`, using the existing oscillator.
|
|
20
|
+
- `oscillator` and `filter`: optional compatible unworklet `defineSubgraph`
|
|
21
|
+
declarations, with the same required config and `tick` signatures as the stock
|
|
22
|
+
modules. Each is instantiated with separate named state for every voice.
|
|
23
|
+
|
|
24
|
+
`src/instrument-example.ts` demonstrates replacing both parts with custom
|
|
25
|
+
unworklet subgraphs: a half-level sine and backward-Euler one-pole low-pass.
|
|
26
|
+
The example is intentionally sine-only and its filter ignores resonance; it
|
|
27
|
+
illustrates a concrete replacement, not another full instrument or a quality
|
|
28
|
+
upgrade. Its amplitude/response is checked against independent formulas.
|
|
29
|
+
|
|
30
|
+
The processor uses unworklet's compile-time sample rate. Offline coverage is
|
|
31
|
+
44.1/48/96 kHz; the published 0.4.1 bundler/browser boundary remains 48 kHz.
|
|
32
|
+
The complete 16-voice graph compiles, but the initial real-time trace found
|
|
33
|
+
frequent deadline overruns at that capacity on the validation host. The engine
|
|
34
|
+
therefore starts at four voices; higher capacities require a device-specific
|
|
35
|
+
real-time budget check. A 32-voice probe produced a
|
|
36
|
+
11,614,744-byte process function before completion-expression materialization, exceeding WebAssembly's 7,654,321-byte function
|
|
37
|
+
limit. The first engine keeps a tested ceiling of 16 at construction;
|
|
38
|
+
16 voices with 256 held identities are tested. These checks establish compilation
|
|
39
|
+
and bounded rendering, not a mobile-device real-time CPU guarantee.
|
|
40
|
+
|
|
41
|
+
## Ports and musical controls
|
|
42
|
+
|
|
43
|
+
Use native `node.midi.midi.send(event)` for MIDI, `node.params.<name>` for
|
|
44
|
+
AudioParams, and `node.events.reset.emit({value:1})` for panic. Output `main` is
|
|
45
|
+
two identical channels. Mixing is linear, with no limiter, normalization or
|
|
46
|
+
voice-count attenuation; coherent voices can exceed unity. The diagnostic gain
|
|
47
|
+
is 0.15, and the browser fixture remains muted.
|
|
48
|
+
|
|
49
|
+
`diagnosticInstrumentParameters` is an ordinary initial AudioParam value map,
|
|
50
|
+
usable with unworklet's existing `createNode(..., {initial: ...})`. It is not a
|
|
51
|
+
separate preset/state format. All continuous controls are a-rate:
|
|
52
|
+
|
|
53
|
+
| Controls | Units / bounds |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `gain` | Linear [0,1] |
|
|
56
|
+
| `ampAttack`, `ampDecay`, `ampRelease` | Seconds [0,30] |
|
|
57
|
+
| `ampSustain` | [0,1] |
|
|
58
|
+
| `pitchAttack`, `pitchDecay`, `pitchRelease`, `pitchSustain` | Same ADSR units |
|
|
59
|
+
| `filterAttack`, `filterDecay`, `filterRelease`, `filterSustain` | Same ADSR units |
|
|
60
|
+
| `cutoff`, `resonance` | Hz [20,20000], Q [0.5,10]; actual cutoff limited to 0.45×sample rate |
|
|
61
|
+
| `pitchEnvelopeDepth`, `lfoPitchDepth` | Semitones [-24,24] |
|
|
62
|
+
| `filterEnvelopeDepth`, `lfoFilterDepth` | Octaves [-8,8] |
|
|
63
|
+
| `lfoRate`, `lfoAmpDepth` | Hz [0,20], tremolo depth [0,1] |
|
|
64
|
+
| `bypass` | Values >=0.5 mute output |
|
|
65
|
+
|
|
66
|
+
Inputs must be finite and within their declared AudioParam domains. Automation
|
|
67
|
+
and any host smoothing use unworklet/Web Audio, not a den wrapper.
|
|
68
|
+
|
|
69
|
+
Each voice has three instances of the same linear ADSR. The amp envelope governs
|
|
70
|
+
voice lifetime: zero sustain does not free a held voice; release completion does.
|
|
71
|
+
Audio ends on the exact envelope sample. The allocator consumes completion once
|
|
72
|
+
at block end, before the next block-boundary MIDI dispatch; it does not perform
|
|
73
|
+
quadratic allocation-rank maintenance on every audio sample.
|
|
74
|
+
Pitch/filter envelopes may finish earlier or later but cannot keep an amp-silent
|
|
75
|
+
voice allocated. Retrigger/release starts from each envelope's current level.
|
|
76
|
+
Duration quantization, segment latching and zero-duration behavior are those of
|
|
77
|
+
`envelope.ts`; edits affect the next segment except sustain, which tracks while
|
|
78
|
+
held in the sustain stage.
|
|
79
|
+
|
|
80
|
+
One shared free-running LFO drives finite amp/pitch/filter destinations, with no
|
|
81
|
+
implicit note reset. For voice i:
|
|
82
|
+
|
|
83
|
+
- Frequency = MIDI equal-tempered A4=440 Hz × 2^(semitones/12), clamped to
|
|
84
|
+
[0,0.45×sampleRate]. Semitones = pitch-envelope level × envelope depth + LFO ×
|
|
85
|
+
LFO pitch depth, with the combined amount clamped to [-24,24].
|
|
86
|
+
- Cutoff = base cutoff × 2^octaves, then clamped to the filter bounds. Octaves =
|
|
87
|
+
filter-envelope level × envelope depth + LFO × LFO filter depth; the combined
|
|
88
|
+
amount is clamped to [-8,8].
|
|
89
|
+
- Amp = amp-envelope level × normalized MIDI velocity ×
|
|
90
|
+
(1 - tremoloDepth × (1 - LFO)/2). Depth zero is unity; depth one spans [0,1].
|
|
91
|
+
|
|
92
|
+
Both pitch and filter depths are combined in their musical units **before** the
|
|
93
|
+
single final destination clamp. A constant 128-note tuning table supplies MIDI
|
|
94
|
+
base pitches; transcendental pitch ratios use the reviewed modulation module.
|
|
95
|
+
|
|
96
|
+
## Event order, reset and state
|
|
97
|
+
|
|
98
|
+
MIDI dispatch remains quantum-boundary FIFO in unworklet 0.4.1. Do not claim
|
|
99
|
+
sample-accurate MIDI. On/off pairs in one block can coalesce to no sound.
|
|
100
|
+
Duplicate identities, velocity-zero note-on, steal/release reuse and mono
|
|
101
|
+
last-held-note priority follow `voice-policy.ts`; CC64 is not implemented.
|
|
102
|
+
|
|
103
|
+
- A note/retrigger resets its oscillator phase and filter history, while its
|
|
104
|
+
envelopes retrigger from their current levels. Mono legato leaves those states
|
|
105
|
+
continuous. Hard stealing/retrigger can click; no listening approval implied.
|
|
106
|
+
- CC123 starts release for that channel. CC120 immediately frees its voices and
|
|
107
|
+
clears their DSP states. If another note reuses a cleared slot in the same
|
|
108
|
+
quantum, the clearing sample is silent and its attack begins next sample.
|
|
109
|
+
- `reset` wins over every MIDI event dispatched in the same quantum. It clears
|
|
110
|
+
the held ledger, voice allocation, pending retriggers, envelopes, oscillators,
|
|
111
|
+
filters and shared LFO. Output is immediately silent. Fresh notes in later
|
|
112
|
+
quanta can start normally.
|
|
113
|
+
- Bypass mutes only the output. MIDI, phase, modulation and release continue; a
|
|
114
|
+
held note reappears when unbypassed, while an ended release remains silent.
|
|
115
|
+
- Inactive slots clear their DSP histories. A new note cannot inherit a completed
|
|
116
|
+
or panicked voice's tail. Live held-note state is transient: same-schema
|
|
117
|
+
snapshots restored into a new node restore parameters/shared LFO but do not
|
|
118
|
+
revive held voices. Inactive
|
|
119
|
+
histories are cleared on the first rendered sample, including when a new note
|
|
120
|
+
arrives in the first quantum after restore. That clearing sample is silent;
|
|
121
|
+
the new note begins at phase zero on the next sample. A pristine instance
|
|
122
|
+
needs no extra clearing sample. Native 0.4.1 **in-place** restore only overlays
|
|
123
|
+
persistent slots: it does not initialize transient live voices. For a cold
|
|
124
|
+
in-place restore, send the existing `reset` after restoring, render at least
|
|
125
|
+
one quantum, then send fresh notes. Sending reset and fresh notes in the same
|
|
126
|
+
quantum follows the documented reset-wins policy. No restore wrapper is added.
|
|
127
|
+
Follow the existing
|
|
128
|
+
rendered-parameter snapshot constraint; schema migration is not added.
|
|
129
|
+
|
|
130
|
+
## Verification and integration
|
|
131
|
+
|
|
132
|
+
`tests/instrument.spec.ts` uses an independent direct-form biquad plus analytic
|
|
133
|
+
sines for the complete signal path, direct closed-form envelopes for per-voice
|
|
134
|
+
release/retrigger, and frequency/cutoff probe replacements for modulation units.
|
|
135
|
+
It tests wrong-answer rejection, stereo equality, zero scrubbed samples,
|
|
136
|
+
polyphony, mono legato, steals, channel panic, reset, edits, bypass and snapshots.
|
|
137
|
+
|
|
138
|
+
The isolated packed fixture checks declarations/build, renders all three offline
|
|
139
|
+
rates, compiles the replacement example, and drives real 48 kHz browser MIDI and
|
|
140
|
+
AudioParams. It produces raw stereo float WAV, a fixed-scale waveform and a
|
|
141
|
+
manifest binding the exact source hashes/commit, package/lock, all diagnostic
|
|
142
|
+
parameters, MIDI, rates, seed and verification. No audio matcher/golden update or
|
|
143
|
+
FFT is used.
|
|
144
|
+
|
|
145
|
+
The public subpaths are `@denaudio/den/instrument` and
|
|
146
|
+
`@denaudio/den/instrument-example`. The isolated packed fixture imports the
|
|
147
|
+
replacement oscillator/filter example through that public boundary and renders
|
|
148
|
+
it at 48 kHz (the stock instrument is rendered at all three offline rates). It does not reach into package internals.
|
|
149
|
+
|
|
150
|
+
## Sustained real-time gate
|
|
151
|
+
|
|
152
|
+
The instrument-specific browser observer records twelve seconds of actual graph
|
|
153
|
+
output at 48 kHz, including four held A4 voices, note-offs and a one-second
|
|
154
|
+
release. Its fixed capture buffer is test instrumentation only. A sinusoid fit
|
|
155
|
+
from the initial steady window predicts the entire steady interval; a separate
|
|
156
|
+
first-difference bound detects discontinuities and the known release law
|
|
157
|
+
predicts the tail. A separate native `AudioBufferSourceNode` supplies an exactly
|
|
158
|
+
representable reference ramp to the observer's second input. The test requires
|
|
159
|
+
4,500 complete 128-sample blocks and exact ramp progression through all 576,000
|
|
160
|
+
samples, including the silent tail. Duplicate, dropped, reordered, zeroed and
|
|
161
|
+
short-block counterexamples verify the observation contract.
|
|
162
|
+
|
|
163
|
+
Published `currentFrame` entry/exit values and output timestamps remain in the
|
|
164
|
+
raw evidence. Repeated/skipped published frame values are reported separately
|
|
165
|
+
as `CLOCK_METADATA_ANOMALY_REQUIRES_REVIEW`: a hosted failing and passing capture
|
|
166
|
+
had byte-identical complete WAVs despite different frame metadata. Chromium's
|
|
167
|
+
worklet-scope clock publication can skip a lock-contended update; this mechanism
|
|
168
|
+
does not by itself establish the cause of an individual anomaly. Native ramp
|
|
169
|
+
progression and the independent audio oracle establish the captured graph's
|
|
170
|
+
sequence, not hardware continuity. The artifact retains `NOT_CLEARED`, the raw
|
|
171
|
+
WAV/reference data, waveform, native errors and timestamps. The expected 0.4.1
|
|
172
|
+
`sab-unavailable` notification is recorded; traps and queue/length errors fail.
|
|
173
|
+
|
|
174
|
+
Chrome audio trace is also collected to inspect per-quantum render durations.
|
|
175
|
+
Raw graph continuity alone cannot establish that the hardware output met its
|
|
176
|
+
real-time deadline. Neither this fixture nor offline correctness substitutes for
|
|
177
|
+
hardware-loopback/device testing or listening approval.
|
|
178
|
+
|
|
179
|
+
The trace report includes generated WASM sizes/hashes, wall and CPU render
|
|
180
|
+
durations, and counts above the 128-frame budget (2.667 ms). It excludes the
|
|
181
|
+
first second when reporting steady timing and tests the detector with an
|
|
182
|
+
explicit over-budget counterexample. Any observed steady wall-time overrun
|
|
183
|
+
sets `QUANTUM_OVERRUNS_REQUIRE_REVIEW`; passing numerical assertions does not
|
|
184
|
+
override this readiness finding. A four-voice measurement on the validation
|
|
185
|
+
host had median 0.344 ms, p99 0.992 ms, maximum 14.752 ms and 9 steady wall-time
|
|
186
|
+
overruns. Sixteen voices had median 1.447 ms, p99 4.583 ms and 221 overruns.
|
|
187
|
+
These are host-specific observations, not portable capacity guarantees. The
|
|
188
|
+
raw trace and each run's exact figures are preserved with the candidate WAV.
|
|
189
|
+
|
|
190
|
+
Bounded pitch/cutoff modulation signals and control predicates use existing
|
|
191
|
+
transient unworklet state, because expression reuse alone can expand the graph.
|
|
192
|
+
Oscillator output, filter output and amplitude-envelope level stay expressions:
|
|
193
|
+
native floating-point state writes flush magnitudes below 1e-30, which would
|
|
194
|
+
destroy finite tiny values recoverable by a later custom filter or gain.
|
|
195
|
+
No private memoization API, altered flush threshold or custom-value restriction
|
|
196
|
+
is used. Regression cases include subnormal input, large oscillator/tiny amp,
|
|
197
|
+
tiny filter output and signed zero observed by a replacement filter.
|
|
198
|
+
Transient layout changes are not a migration for snapshots from older revisions;
|
|
199
|
+
the snapshot contract remains identical processor/schema/rate only.
|
|
200
|
+
Voice completion is committed once per block after
|
|
201
|
+
sample processing; this avoids running allocator rank maintenance at audio rate
|
|
202
|
+
while preserving exact sample silence and availability at the next MIDI drain.
|
|
203
|
+
Shared DSP modules remain unchanged; the common expression-expansion/runtime
|
|
204
|
+
performance investigation is still outstanding.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Mobile integration candidate
|
|
2
|
+
|
|
3
|
+
The initial integration candidate is based on reviewed candidate
|
|
4
|
+
`196a1c3ef0e8eda9a007a2474c9d06f7f654acfa` (PR26, including PR28/PR29)
|
|
5
|
+
and main `9fd730bf9afb786edaae808d41208904fb3c844d`. This integration completes
|
|
6
|
+
public subpath access and the permanent packed-site build without changing DSP,
|
|
7
|
+
settings or dependencies. It is not a registry release or a real-time clearance.
|
|
8
|
+
|
|
9
|
+
The owner supplied positive feedback on the three dry sounds and the integrated
|
|
10
|
+
sound/FX preview on 2026-10-05. That candidate listening feedback is recorded in
|
|
11
|
+
[the acceptance record](initial-acceptance.md). No identical listening request is
|
|
12
|
+
pending. Exact-hash golden promotion remains separate; general physical-device
|
|
13
|
+
stability and the instrument runtime gate remain NOT_CLEARED.
|
|
14
|
+
|
|
15
|
+
The isolated `tests/integration-consumer` imports the public `./instrument` and
|
|
16
|
+
`./delay-fx` and `./delay-settings` subpaths from the installed package. The instrument is a processor;
|
|
17
|
+
the delay uses a small processor adapter around the existing subgraph. Its stereo
|
|
18
|
+
Web Audio path is MIDI → instrument → delay → master gain → analyser → destination.
|
|
19
|
+
MIDI remains the existing unworklet quantum-boundary interface. No new MIDI,
|
|
20
|
+
parameter, snapshot, serialization, routing or preset framework is introduced.
|
|
21
|
+
|
|
22
|
+
The sound selector offers Diagnostic, Bass, Percussion and Pad. The three sound
|
|
23
|
+
candidates are the unchanged public `createInstrument` configurations and all 22
|
|
24
|
+
native parameters from PR28, merged into this candidate branch at
|
|
25
|
+
`e87e51f25052d0ad474efe1a928f80908cf3bcaf`. Source SHA-256 checks pin their audio
|
|
26
|
+
identity. These remain sound candidates: no golden promotion or general runtime clearance
|
|
27
|
+
is recorded by this integration. Existing candidate listening feedback is retained
|
|
28
|
+
separately from machine-generated evidence.
|
|
29
|
+
|
|
30
|
+
Diagnostic retains four sine voices, gain .05, fixed 1 kHz/Q=.5 low-pass and
|
|
31
|
+
10 ms attack/200 ms release. Bass is mono last-held legato and Percussion mono
|
|
32
|
+
retriggered; both show low-register keys and a single C2 hold/trigger button.
|
|
33
|
+
Their controls do not promise a chord. Pad has four saw voices with a slow
|
|
34
|
+
attack/release, and C3/G3/C4/E4 keys plus their chord. Every selector is disabled
|
|
35
|
+
during loading, playback and teardown. Active synthetic changes are ignored,
|
|
36
|
+
without resetting held notes, parameters or the graph. Stopped sound selection
|
|
37
|
+
updates the labels/notes only; Start creates the selected public processor with
|
|
38
|
+
its exact native initial parameters. FX selection restores that FX's mix and
|
|
39
|
+
feedback defaults.
|
|
40
|
+
|
|
41
|
+
The stereo effects remain Diagnostic delay, Chorus and Rhythmic delay.
|
|
42
|
+
Diagnostic taps are 125/187.5 ms with one-second allocation. Chorus and rhythmic
|
|
43
|
+
use the exported GEN-621 constants: 18 ms chorus taps with .65 Hz/3 ms modulation
|
|
44
|
+
and feedback 0/mix .45; rhythmic uses 120 BPM dotted-eighth/quarter taps, 3.2 kHz
|
|
45
|
+
feedback low-pass and feedback .48/mix .35. Both allocate two seconds. Feedback
|
|
46
|
+
is limited to 0–.5 and wet mix to 0–1. No effect was added.
|
|
47
|
+
|
|
48
|
+
Output gain now spans 0–1, default 1. The previous diagnostic-only 2× ceiling
|
|
49
|
+
was reduced visibly in the UI because its .8 bound cannot be transferred to
|
|
50
|
+
saw/modulated sources. All raw instrument parameters and gain values remain
|
|
51
|
+
unchanged; there is no normalization, limiter or hidden per-sound compensation.
|
|
52
|
+
See [the sound/FX headroom evidence](sound-audition-headroom.md) for the distinct
|
|
53
|
+
bounds and measured 4×3 matrix. Both stereo channels must be finite, unscrubbed,
|
|
54
|
+
unclipped and within the explicit fixture bounds. The analyser display remains
|
|
55
|
+
a mono monitor with fixed −1 to +1 scale.
|
|
56
|
+
|
|
57
|
+
Start unlocks a fresh 48 kHz AudioContext in a real gesture. Holds may begin during
|
|
58
|
+
asynchronous loading and retain their latest intent. Release sends note-offs and
|
|
59
|
+
allows the delay tail to decay. Clear sends the existing instrument reset and a
|
|
60
|
+
delay-adapter reset event. Stop fades master gain for 20 ms, disposes both nodes,
|
|
61
|
+
and closes the context. Partial startup failure/cancellation cannot connect a
|
|
62
|
+
late node. Hidden-page teardown and touch/keyboard cancellation are retained.
|
|
63
|
+
|
|
64
|
+
Run `CHROMIUM_PATH=/usr/bin/chromium node --test tests/integration.test.mjs` on a
|
|
65
|
+
host with that browser, or omit the variable for the pinned Playwright browser.
|
|
66
|
+
The fixture uses the existing pack/install/build helper, a separate locked
|
|
67
|
+
consumer, independent analytic/direct-form references, and browser taps of the
|
|
68
|
+
actual connected instrument/FX outputs. It retains candidate WAV, settings,
|
|
69
|
+
source/package/WASM hashes, every numerical peak, browser results and failures
|
|
70
|
+
under `artifacts/integration`. Generated evidence is never an approved golden.
|
|
71
|
+
`npm run build:integration` produces a standalone integration-only local build.
|
|
72
|
+
The previous public UI has been removed. `npm run build:site` now clears the
|
|
73
|
+
public output and writes only a minimal rebuilding notice, with no old page or
|
|
74
|
+
audio asset retained. Historical combined pages are staged only by
|
|
75
|
+
`tests/site-fixture-build.mjs` in a temporary directory for automated checks.
|
|
76
|
+
Their locked package integrity/collision guards remain unchanged.
|
|
77
|
+
|
|
78
|
+
`tests/site-fixture.test.mjs` retains the former selected sound/FX, silent
|
|
79
|
+
snapshot, audition, navigation and asset assertions. The full matrix and
|
|
80
|
+
interrupted/repeated lifecycle checks remain in `tests/integration.test.mjs`.
|
|
81
|
+
`tests/site.test.mjs` separately verifies the public deletion and removed-route
|
|
82
|
+
404s. No production branch/project, authentication, access or header setting
|
|
83
|
+
change is needed. Removal publication follows independent review and exact-head
|
|
84
|
+
CI; verification fixtures do not retain the former UI as a product.
|
|
85
|
+
|
|
86
|
+
The packed and realtime browser gates use Vite preview with `configFile:false`,
|
|
87
|
+
without COOP/COEP headers. The integration manifest records response isolation
|
|
88
|
+
headers, `crossOriginIsolated`, SharedArrayBuffer availability, and the actual
|
|
89
|
+
transport passed to each native AudioWorkletNode. Both instrument and delay are
|
|
90
|
+
verified using `postMessage`. `vercel.json` does not configure isolation headers;
|
|
91
|
+
production browser observation reported the same fallback warning. Direct public
|
|
92
|
+
response-header inspection from this workspace was blocked by the network proxy
|
|
93
|
+
(CONNECT 403), so upstream hosting/proxy headers are not independently verified.
|
|
94
|
+
This establishes fallback coverage, not identical phone scheduling or physical
|
|
95
|
+
audio behavior. No hosting/header settings are changed.
|
|
96
|
+
|
|
97
|
+
## Verification coverage
|
|
98
|
+
|
|
99
|
+
The packed numerical matrix covers all four sounds and three FX at dry, default,
|
|
100
|
+
and maximum wet/feedback. Poly cases include coherent four-voice stress; Pad
|
|
101
|
+
runs beyond a full slow-LFO cycle. Existing independent diagnostic oscillator,
|
|
102
|
+
filter and delay references remain, and the new matrix compares each FX against
|
|
103
|
+
an independent per-sample reference using its actual input.
|
|
104
|
+
|
|
105
|
+
Packed browser checks exercise all twelve selected graphs, all 22 native initial
|
|
106
|
+
parameters, mono/poly note labels, stopped-only selectors, no autoplay, mobile
|
|
107
|
+
layout, default/dry/max-wet-feedback stereo peaks, mute, release, reset and clean
|
|
108
|
+
restart. Existing startup-failure, partial-load cancellation, touch cancellation,
|
|
109
|
+
keyboard renewal, context cleanup, and 15 ms target/20 ms Stop-fade checks remain.
|
|
110
|
+
Screenshots and measurement JSON are retained with candidate artifacts. Browser
|
|
111
|
+
CI is required; a local numerical pass is not a browser pass. Runtime remains
|
|
112
|
+
NOT_CLEARED and generated evidence remains CANDIDATE. Production branch/project
|
|
113
|
+
settings are unchanged; merging this reviewed build configuration deploys the
|
|
114
|
+
initial candidate through the existing Vercel integration.
|