@denaudio/den 0.0.0-stage → 0.1.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.
Files changed (191) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE-APACHE +202 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +102 -2
  5. package/dist/biquad-eq.d.ts +11 -0
  6. package/dist/biquad-eq.js +35 -0
  7. package/dist/catalog-filter-math.d.ts +4 -0
  8. package/dist/catalog-filter-math.js +30 -0
  9. package/dist/character-filter.d.ts +13 -0
  10. package/dist/character-filter.js +53 -0
  11. package/dist/convolution-spectrum.d.ts +25 -0
  12. package/dist/convolution-spectrum.js +82 -0
  13. package/dist/convolution.d.ts +15 -0
  14. package/dist/convolution.js +81 -0
  15. package/dist/crossover.d.ts +11 -0
  16. package/dist/crossover.js +17 -0
  17. package/dist/curve-shaper.d.ts +26 -0
  18. package/dist/curve-shaper.js +73 -0
  19. package/dist/delay-fx.d.ts +37 -0
  20. package/dist/delay-fx.js +84 -0
  21. package/dist/delay-readhead.d.ts +19 -0
  22. package/dist/delay-readhead.js +54 -0
  23. package/dist/delay-settings.d.ts +55 -0
  24. package/dist/delay-settings.js +20 -0
  25. package/dist/drive.d.ts +24 -0
  26. package/dist/drive.js +116 -0
  27. package/dist/dual-head-delay.d.ts +28 -0
  28. package/dist/dual-head-delay.js +77 -0
  29. package/dist/dynamics.d.ts +61 -0
  30. package/dist/dynamics.js +130 -0
  31. package/dist/envelope.d.ts +34 -0
  32. package/dist/envelope.js +62 -0
  33. package/dist/filter.d.ts +9 -0
  34. package/dist/filter.js +34 -0
  35. package/dist/filterbank-vocoder.d.ts +35 -0
  36. package/dist/filterbank-vocoder.js +52 -0
  37. package/dist/formant-bank.d.ts +15 -0
  38. package/dist/formant-bank.js +28 -0
  39. package/dist/freeze-reverb.d.ts +35 -0
  40. package/dist/freeze-reverb.js +61 -0
  41. package/dist/frequency-shifter.d.ts +24 -0
  42. package/dist/frequency-shifter.js +57 -0
  43. package/dist/gate.d.ts +2 -0
  44. package/dist/gate.js +12 -0
  45. package/dist/index.d.ts +7 -0
  46. package/dist/index.js +12 -0
  47. package/dist/instrument-example.d.ts +12 -0
  48. package/dist/instrument-example.js +29 -0
  49. package/dist/instrument-settings.d.ts +95 -0
  50. package/dist/instrument-settings.js +36 -0
  51. package/dist/instrument.d.ts +42 -0
  52. package/dist/instrument.js +166 -0
  53. package/dist/lfo.d.ts +19 -0
  54. package/dist/lfo.js +55 -0
  55. package/dist/live-buffer.d.ts +39 -0
  56. package/dist/live-buffer.js +52 -0
  57. package/dist/live-granular.d.ts +41 -0
  58. package/dist/live-granular.js +64 -0
  59. package/dist/lookahead-limiter.d.ts +31 -0
  60. package/dist/lookahead-limiter.js +67 -0
  61. package/dist/loop-crossfade.d.ts +28 -0
  62. package/dist/loop-crossfade.js +72 -0
  63. package/dist/modulation-fx.d.ts +47 -0
  64. package/dist/modulation-fx.js +58 -0
  65. package/dist/modulation.d.ts +132 -0
  66. package/dist/modulation.js +228 -0
  67. package/dist/mpe-expression.d.ts +36 -0
  68. package/dist/mpe-expression.js +73 -0
  69. package/dist/multiband-dynamics.d.ts +50 -0
  70. package/dist/multiband-dynamics.js +60 -0
  71. package/dist/musical-controls.d.ts +76 -0
  72. package/dist/musical-controls.js +125 -0
  73. package/dist/musical-examples.d.ts +21 -0
  74. package/dist/musical-examples.js +142 -0
  75. package/dist/musical-materials.d.ts +110 -0
  76. package/dist/musical-materials.js +56 -0
  77. package/dist/oscillator.d.ts +10 -0
  78. package/dist/oscillator.js +35 -0
  79. package/dist/oversampled-drive.d.ts +19 -0
  80. package/dist/oversampled-drive.js +99 -0
  81. package/dist/performance.d.ts +61 -0
  82. package/dist/performance.js +241 -0
  83. package/dist/pitch-quantizer.d.ts +26 -0
  84. package/dist/pitch-quantizer.js +88 -0
  85. package/dist/prepared-convolution.d.ts +22 -0
  86. package/dist/prepared-convolution.js +99 -0
  87. package/dist/resident-recorder.d.ts +28 -0
  88. package/dist/resident-recorder.js +68 -0
  89. package/dist/resident-time-stretch.d.ts +44 -0
  90. package/dist/resident-time-stretch.js +135 -0
  91. package/dist/resonator.d.ts +32 -0
  92. package/dist/resonator.js +63 -0
  93. package/dist/reverb.d.ts +27 -0
  94. package/dist/reverb.js +52 -0
  95. package/dist/sample.d.ts +113 -0
  96. package/dist/sample.js +212 -0
  97. package/dist/source.d.ts +58 -0
  98. package/dist/source.js +113 -0
  99. package/dist/spatial-chains.d.ts +48 -0
  100. package/dist/spatial-chains.js +76 -0
  101. package/dist/spectral-fft.d.ts +21 -0
  102. package/dist/spectral-fft.js +74 -0
  103. package/dist/spectral-freeze.d.ts +22 -0
  104. package/dist/spectral-freeze.js +74 -0
  105. package/dist/spectral-gate.d.ts +21 -0
  106. package/dist/spectral-gate.js +58 -0
  107. package/dist/spectral-stft.d.ts +13 -0
  108. package/dist/spectral-stft.js +69 -0
  109. package/dist/spectral-texture.d.ts +42 -0
  110. package/dist/spectral-texture.js +141 -0
  111. package/dist/spectral.d.ts +16 -0
  112. package/dist/spectral.js +10 -0
  113. package/dist/state-variable-filter.d.ts +17 -0
  114. package/dist/state-variable-filter.js +25 -0
  115. package/dist/stereo-delay.d.ts +45 -0
  116. package/dist/stereo-delay.js +70 -0
  117. package/dist/virtual-analog.d.ts +26 -0
  118. package/dist/virtual-analog.js +66 -0
  119. package/dist/voice-policy.d.ts +37 -0
  120. package/dist/voice-policy.js +245 -0
  121. package/dist/wavetable-bands.d.ts +37 -0
  122. package/dist/wavetable-bands.js +145 -0
  123. package/dist/wavetable-read.d.ts +6 -0
  124. package/dist/wavetable-read.js +11 -0
  125. package/dist/wavetable.d.ts +29 -0
  126. package/dist/wavetable.js +49 -0
  127. package/dist/windowed-pitch-shift.d.ts +34 -0
  128. package/dist/windowed-pitch-shift.js +51 -0
  129. package/docs/catalog-audition.md +76 -0
  130. package/docs/catalog-filters.md +125 -0
  131. package/docs/catalog-modulation-fx.md +220 -0
  132. package/docs/catalog-status.md +431 -0
  133. package/docs/character-filter.md +139 -0
  134. package/docs/contracts.md +131 -0
  135. package/docs/control-modulation.md +194 -0
  136. package/docs/convolution.md +45 -0
  137. package/docs/curve-shaper.md +117 -0
  138. package/docs/delay-fx-capture-findings.json +134 -0
  139. package/docs/delay-fx.md +202 -0
  140. package/docs/delay-readhead.md +121 -0
  141. package/docs/delay-settings-candidate.json +333 -0
  142. package/docs/delay-settings.md +111 -0
  143. package/docs/dependency-findings.md +61 -0
  144. package/docs/deployment-status.md +37 -0
  145. package/docs/drive.md +152 -0
  146. package/docs/dual-head-delay.md +131 -0
  147. package/docs/dynamics.md +180 -0
  148. package/docs/envelope-integer-staging.md +64 -0
  149. package/docs/filter-materialization-results.json +71 -0
  150. package/docs/filter-materialization.md +135 -0
  151. package/docs/filter-oscillator.md +126 -0
  152. package/docs/filterbank-vocoder-entry.md +58 -0
  153. package/docs/freeze-reverb.md +179 -0
  154. package/docs/frequency-shifter.md +133 -0
  155. package/docs/frontend-audition.md +22 -0
  156. package/docs/gain-schedule-verification.md +53 -0
  157. package/docs/initial-acceptance.md +79 -0
  158. package/docs/instrument-performance.md +191 -0
  159. package/docs/instrument-settings.md +130 -0
  160. package/docs/instrument.md +204 -0
  161. package/docs/integration-candidate.md +114 -0
  162. package/docs/live-buffer.md +163 -0
  163. package/docs/live-granular.md +179 -0
  164. package/docs/lookahead-multiband.md +183 -0
  165. package/docs/loop-crossfade.md +99 -0
  166. package/docs/mpe-expression.md +65 -0
  167. package/docs/musical-clock-seek-boundary.md +31 -0
  168. package/docs/musical-controls-contract.md +144 -0
  169. package/docs/musical-examples.md +255 -0
  170. package/docs/oversampled-drive.md +153 -0
  171. package/docs/performance.md +53 -0
  172. package/docs/pitch-quantizer.md +55 -0
  173. package/docs/prepared-convolution.md +188 -0
  174. package/docs/realtime-audition.md +63 -0
  175. package/docs/resident-recorder.md +151 -0
  176. package/docs/resident-time-stretch.md +190 -0
  177. package/docs/sample.md +191 -0
  178. package/docs/sound-audition-headroom.md +103 -0
  179. package/docs/sources.md +81 -0
  180. package/docs/spatial-chains-entry.md +250 -0
  181. package/docs/spectral-freeze-entry.md +130 -0
  182. package/docs/spectral-gate-entry.md +130 -0
  183. package/docs/spectral-large-candidate.md +74 -0
  184. package/docs/spectral-texture-entry.md +174 -0
  185. package/docs/spectral.md +118 -0
  186. package/docs/virtual-analog.md +77 -0
  187. package/docs/voice-policy.md +38 -0
  188. package/docs/wavetable-bands.md +138 -0
  189. package/docs/wavetable.md +101 -0
  190. package/docs/windowed-pitch-shift.md +112 -0
  191. package/package.json +269 -4
@@ -0,0 +1,179 @@
1
+ # Bounded FDN freeze-tail candidate
2
+
3
+ Status: CANDIDATE / NOT_CLEARED. Native mechanical evidence does not establish
4
+ musical approval, browser behavior, or realtime deadline safety.
5
+
6
+ ## Catalog fit and public entry
7
+
8
+ Original [catalog §10](https://app.notion.com/p/3ef449b4e81d817db1acf1fff48779b3)
9
+ separates algorithmic FDN, convolution, and special tails including freeze/shimmer.
10
+ This is a bounded special-tail extension: four unequal integer delays with a
11
+ Hadamard/2 feedback matrix and an excitation/loss transition into a frozen loop.
12
+ It does not finish the whole catalog row or implement shimmer. The existing
13
+ `algorithmicReverb` remains its own damped/interpolated candidate.
14
+
15
+ Import `freezeReverb`, `FreezeReverbConfig`, and `FreezeReverbControls` from
16
+ `@denaudio/den/freeze-reverb`. Instantiate with the enclosing `ctx.sampleRate` and
17
+ call `tick(left, right, { freeze, reset })` once per audio sample. It returns wet-only
18
+ `left`, `right`, `freezeAmount` (f32 [0,1]) and `frozen` (boolean). A consumer owns
19
+ its dry/wet blend, bypass behavior, gain staging and any separate tone filters.
20
+ No input/output limiter or automatic audio normalization is applied.
21
+
22
+ ## Fixed configuration and bounded capacity
23
+
24
+ - `sampleRate`: integer 8000..192000, required
25
+ - `roomScale`: finite [0.5,2], default 1
26
+ - `decaySeconds`: finite [0.1,10], default 1.5; nominal unfrozen loop-loss time
27
+ - `transitionSamples`: integer 0..192000, default 256; each direction uses the
28
+ same speed, one integer progress step per processed sample
29
+
30
+ Out-of-contract configuration throws at graph construction. Rebuild for other
31
+ configuration. unworklet eagerly captures at 48 kHz as well as the requested
32
+ rate; use `ctx.sampleRate` rather than a captured host rate.
33
+
34
+ Line lengths are round(sampleRate * roomScale * [0.0297, 0.0371, 0.0411, 0.0437])
35
+ integer samples. Each line allocates exactly that many f64 values, one i32 cursor
36
+ and one i32 valid count, with one additional shared i32 freeze-progress counter.
37
+ Four transient f64 tap slots materialize all reads before any history write.
38
+ At 192 kHz / scale 2 the lengths are 11405, 14246, 15782, 16781: 58214 f64 values,
39
+ 465712 history bytes. There are no dynamically growing buffers or per-sample
40
+ allocations. Actual compiled memory includes runtime and port overhead.
41
+
42
+ There is no predelay, FFT framing, lookahead or host-compensation latency.
43
+ The first wet arrival is the shortest fixed network delay; dry is absent. Delay
44
+ lengths never move, so freeze transitions introduce no moving-readhead Doppler.
45
+
46
+ ## Excitation, loss and transition contract
47
+
48
+ Let H be the four-row Hadamard matrix, M=H/2, d_i each delay length and
49
+ q_i=10^(-3*d_i/(sampleRate*decaySeconds)). The outgoing delay samples are x_i.
50
+ For a freeze amount a in [0,1], the line feedback gains are a+(1-a)*q_i.
51
+ The new delay samples are M*diag(gains)*x + (1-a)*B*[left,right], where B contains
52
+ the first two orthonormal Hadamard columns. Wet output projects x through the
53
+ last two orthonormal rows. The output is read before the current input is stored.
54
+
55
+ - Live (a=0): excitation is admitted and each traversal loses energy according
56
+ to its fixed q_i. There is no damping lowpass or frequency-selective decay.
57
+ - Freezing: progress increments before processing this sample. From live, N true
58
+ samples reach a=1. Input admission falls while feedback gains rise.
59
+ - Fully frozen (a=1): excitation is exactly suppressed and every feedback gain
60
+ is 1. Input changes cannot alter the stored tail. Output continues to observe
61
+ the circulating state; reading output does not consume its stored energy.
62
+ - Thawing: progress decrements before processing this sample. Loss and excitation
63
+ return over N samples. Reversing direction continues from the current progress;
64
+ it does not restart a full ramp. Held controls settle exactly at their endpoint.
65
+ - N=0: immediate endpoints. This and abrupt input/mode changes can click. A ramp
66
+ reduces coefficient jumps but is not a universal click-free guarantee.
67
+ - Reset has highest priority: this sample's wet output is zero, input is discarded,
68
+ cursors/valid counts and progress become zero even when freeze is held. Repeated
69
+ reset repeats this behavior. After release, the requested mode ramps from live.
70
+
71
+ Reset logically invalidates history in constant time; it does not physically
72
+ zero every buffer cell. Valid counts prevent stale data from reappearing. An
73
+ empty network frozen immediately remains silent. A freeze requested immediately
74
+ with a nonzero transition can still admit excitation during that transition.
75
+
76
+ ## Stability, precision and headroom
77
+
78
+ In real arithmetic, M is orthogonal. With zero excitation, each delay-state update
79
+ is nonexpansive for every permitted gain, even during arbitrary mode reversals.
80
+ The fully frozen network is lossless in that mathematical model. During sustained
81
+ live excitation the fixed q_max<1 supplies a small-gain bound; for finite-energy
82
+ input and initially empty state, the wet l2 norm is at most input l2/(1-q_max).
83
+ This is conservative, not an output-peak limit or a promise of a particular RT60.
84
+ The frozen output can continue indefinitely in the ideal model; there is no finite
85
+ frozen-tail cutoff. Use thaw to decay or reset to silence immediately.
86
+
87
+ Implementation feedback/history use f64 and output uses f32. Rounding makes
88
+ perpetual exact energy preservation impossible to promise. Native tests measure
89
+ stored-buffer energy drift, rather than treating fluctuating output-window energy
90
+ as the stored energy. Very-small-state handling is also finite. Exact power-of-two
91
+ internal scaling by 2^256 keeps ordinary and tested tiny f32 signals away from
92
+ unworklet's tiny-state scrub threshold; it does not normalize the audio or restore
93
+ precision lost in floating-point arithmetic.
94
+
95
+ Supply finite normalized audio with known headroom. The feedback network can
96
+ produce wet peaks above 1; tests retain that behavior. No promise is made for
97
+ arbitrary full-scale f32 magnitude, nonfinite inputs, arbitrarily long precision,
98
+ or edited/corrupt snapshots. The implementation neither relies on scrubbing for
99
+ stability nor presents scrubbed renders as passing evidence.
100
+
101
+ The integer delays avoid interpolation loss, but a four-line undamped frozen FDN
102
+ can sound metallic, sparse, periodic or colored. Orthogonality and numerical
103
+ correctness do not establish attractive room/hall/plate sound, pleasant freeze
104
+ transitions, echo density, stereo image, or musical usefulness. No physical-room,
105
+ commercial-reverb equivalence or high-quality shimmer claim is made.
106
+
107
+ ## Snapshot and verification contract
108
+
109
+ Use existing unworklet snapshots only. All four delay histories, cursors, valid
110
+ counts and ramp progress persist. Transient tap materialization is recomputed each
111
+ sample. With the same configuration, sample rate, schema and correctly coordinated
112
+ controls/input, native quantum-boundary restoration continues bit-for-bit,
113
+ including snapshots during a transition or fully frozen nonzero history. Cross-rate,
114
+ capacity, configuration or schema migration is not provided by this module.
115
+
116
+ `tests/freeze-reverb.spec.ts` uses an independent absolute-time scalar history
117
+ model, with expanded matrix formulas and no circular-buffer or DSL implementation.
118
+ At 44.1/48/96 kHz it checks unequal delays and exact impulse arrivals, live/freeze/
119
+ thaw and reversals, transition extremes, one/held reset while frozen, excitation
120
+ suppression, thaw decay, fresh frozen silence, normalized extremes, tiny excitation,
121
+ full-state continuation and measured stored-energy drift. The drift limit is set
122
+ at 2e-10 relative over 262144 frozen samples before collecting results. Numerical
123
+ oracle tolerances are 3e-7 for normal fixtures and 2e-6 for sustained extreme-input
124
+ fixtures. All in-contract native renders require zero scrubs and finite outputs.
125
+
126
+ `tests/freeze-reverb-packed.test.mjs` packs the actual package, installs it into a
127
+ fresh retained consumer, checks the actual public subpath and types, and executes
128
+ native WASM with a-rate controls at all three rates against the scalar oracle.
129
+ It checks same-schema frozen continuation, fixed memory, all first arrivals at
130
+ maximum allocation, and records source/package/WASM/audio hashes. Candidate WAVs
131
+ retain raw wet gain. Node process timing is diagnostic only, excludes copies and
132
+ is not a browser, hardware, or deadline certificate. Failure logs and consumers
133
+ are retained for inspection. No host-render replacement stands in for native DSP.
134
+
135
+ Implementation is original TypeScript/unworklet composition. No third-party DSP
136
+ code or audio assets were copied. The catalog's
137
+ [lossless FDN paper](https://arxiv.org/abs/1606.07729) motivates the distinction
138
+ between mathematical losslessness and actual reverb quality; it is not validation
139
+ of this implementation. Listen to hashed CANDIDATE material before musical approval.
140
+
141
+ The separate `tests/capture-freeze-browser.test.mjs` requires actual 48 kHz
142
+ worklet freeze/thaw, nonzero paired histories, input suppression, divergent live
143
+ state followed by native snapshot restoration, and held reset/empty freeze.
144
+ These functional browser windows do not measure total stored energy or assert
145
+ sample-aligned continuation; the independent offline oracles retain that scope.
146
+ The actual browser stage remains an exact-head CI requirement before merge.
147
+
148
+ ## Live browser restore ordering
149
+
150
+ Core 0.4.1 `node.restore()` first asks the worklet to copy persistent states and
151
+ buffers, awaits its acknowledgement, then writes the saved values to host
152
+ AudioParams. The operation is **not atomic across DSP state and AudioParams**.
153
+ While the node runs, even one quantum with old live/unfrozen controls can inject
154
+ new excitation into newly restored frozen buffers before the saved freeze value
155
+ arrives. This is a real execution boundary, not a waveform tolerance issue.
156
+
157
+ The original PR38 and stacked PR39 browser runs observed paired-left residual
158
+ 0.2498931884765625 after restoring equal frozen histories into differently excited
159
+ live instances. Independent native reproduction with the identical 48 kHz WASM
160
+ and one stale-control quantum produced that exact value (right residual
161
+ 0.4752197265625). Longer gaps also diverged. Excluded wet scratch is recomputed
162
+ every sample and was not the cause: poisoning it with ±1e90 did not alter the
163
+ correctly coordinated result. The failed hosted artifacts remain preserved.
164
+
165
+ For this frozen-tail composition, prepare **all saved controls before restore**:
166
+ freeze=true, level=0, the saved disturbance/input control, and reset=false. Advance
167
+ the native audio clock until those values have rendered and the configured freeze
168
+ transition has completed. Confirm the two histories still differ, then restore the
169
+ saved persistent history while those controls remain stable. At full freeze, new
170
+ excitation is suppressed during any acknowledgement gap. The actual browser gate
171
+ observes native control telemetry and preserves exact paired-zero/nonzero-tail
172
+ assertions after restoration. Its earlier raw restore remains an observation;
173
+ a deterministic native stale-gap regression supplies reproducible negative proof.
174
+
175
+ This protocol uses existing controls and restore only. It adds no synchronization
176
+ hook, serializer or upstream patch, and is not a universal recipe for arbitrary
177
+ live graphs, changing automation, pending events or external audio. Fresh-instance
178
+ offline restoration and correctly coordinated native continuation remain separate
179
+ from unguarded browser restoration across different controls.
@@ -0,0 +1,133 @@
1
+ # Bounded frequency shifter candidate
2
+
3
+ `@denaudio/den/frequency-shifter` exports `frequencyShifter`,
4
+ `FrequencyShifterConfig`, `FrequencyShifterControls`, and
5
+ `FREQUENCY_SHIFTER_LATENCY_SAMPLES` (31). This is an editable unworklet 0.4.1
6
+ subgraph. It remains **CANDIDATE**, with no listening approval or realtime
7
+ clearance. It performs additive signed-Hz translation. Musical pitch-ratio
8
+ shifting, dual-readhead transitions, time stretching and ideal arbitrary-band
9
+ single-sideband processing remain separate work.
10
+
11
+ ## Entry contract
12
+
13
+ Instantiate with `{ sampleRate }`, an integer in [8000, 192000], using an explicit
14
+ instance name. Call `tick(input, controls)` exactly once per sample inside the
15
+ caller's native stride-1 `forSample`. Compose two instances for stereo; no hidden
16
+ channel routing or shared phase state exists.
17
+
18
+ - Input: finite normalized mono audio in [-1, 1]. It is not clipped internally.
19
+ - `shiftHz`: finite f32 audio/control node, clamped to [-sampleRate/4,
20
+ sampleRate/4]. Positive shifts raise each positive input frequency; negative
21
+ shifts lower it while it remains above DC. Units are Hz, never semitones.
22
+ - `mix`: finite f32 node, clamped to [0, 1], linear blend of delayed real input
23
+ and shifted output. Dry and wet have matched 31-sample group delay.
24
+ - `bypass`: bool node selecting the **31-sample-delayed dry** path. Input history
25
+ and modulation phase keep advancing, so returning wet uses current history.
26
+ - `reset`: bool node with level semantics. Every high sample silences the output,
27
+ discards its input, invalidates all prior history and holds modulation phase
28
+ zero. The first subsequent low sample is accepted and uses phase zero.
29
+
30
+ Output is a mono f32 node. Runtime control edits are immediate and unsmoothed.
31
+ Changing mix or bypass can click. Signed shift changes preserve phase, but rapid
32
+ modulation creates additional sidebands. Apply external smoothing/filtering as
33
+ appropriate; there is no hidden parameter framework.
34
+
35
+ ## Filter, phase, latency and headroom
36
+
37
+ The imaginary path is a fixed 63-tap, odd-antisymmetric, Blackman-windowed
38
+ discrete-time Hilbert FIR. The real path is delayed 31 samples. The two zero
39
+ endpoints and antisymmetry permit 15 paired multiply-adds with 30 history reads.
40
+ The independent reference derives its full kernel from the inverse DTFT of
41
+ `-i sign(omega)` and evaluates absolute-time convolution, without importing DSP
42
+ coefficients or reproducing its circular buffer.
43
+
44
+ At output sample n, the shifted signal is
45
+ `delayedReal[n] * cos(phase[n]) - hilbert[n] * sin(phase[n])`.
46
+ The phase advances by `2*pi*shiftHz/sampleRate` **after** output. Modulation phase
47
+ is relative to output time; an input cosine at frequency f has delayed real-path
48
+ phase `-2*pi*f*31/sampleRate`. This convention is checked with a complex DFT,
49
+ including both signs of shift.
50
+
51
+ 31 samples is group delay: 0.703 ms at 44.1 kHz, 0.646 ms at 48 kHz and 0.323 ms
52
+ at 96 kHz. It is not a guarantee that the first 31 wet samples are zero. The FIR
53
+ has pre/post ringing around its center and a startup transient. Dry and bypass
54
+ are exact 31-sample delays, with zero unavailable history.
55
+
56
+ Zero shift holds the existing modulation phase. A fresh/reset instance with zero
57
+ shift is delayed dry identity. Returning to zero after prior shifting generally
58
+ leaves a fixed real/Hilbert phase rotation; it does not force identity.
59
+
60
+ There is no limiter. The FIR's absolute coefficient sum is approximately
61
+ 2.345624. A conservative peak bound is 3.346 for normalized input and the
62
+ documented mix range, including tiny numerical allowance. Tests include an input
63
+ whose output exceeds 2.34, so unity headroom must not be assumed.
64
+
65
+ History and centered phase use exact power-of-two f64 scaling to avoid the
66
+ upstream 0.4.1 state-write threshold erasing tiny finite f32 input and tiny signed
67
+ phase increments near zero. This does not remove normal floating-point limits:
68
+ an increment below the accumulator's current f64 spacing can still be lost.
69
+ Native snapshots retain history, phase, cursor and history validity. The running
70
+ quadrature sum is transient and recomputed on every sample. Restore is tested
71
+ bit-identically only for the same graph/schema and sample rate.
72
+
73
+ ## Useful band and alias limitations
74
+
75
+ The numerical acceptance band is input positive frequencies from 0.05 to 0.45
76
+ times sample rate, with desired and image measurement frequencies kept distinct
77
+ and away from DC/Nyquist. At 48 kHz this is 2.4–21.6 kHz; this short FIR is not a
78
+ broadband low-frequency voice pitch shifter.
79
+
80
+ Independent coefficient DTFT checks sample 4,001 frequencies in that band. The
81
+ complex shifted-sinusoid tests measure five interior/near-edge carriers, in both
82
+ shift directions, at offline 44.1/48/96 kHz. Their acceptance thresholds are:
83
+
84
+ - Centered Hilbert real leakage below 1e-13 and imaginary gain error below 0.00035
85
+ on the coefficient grid
86
+ - Desired/image rejection above 70 dB in the rendered probes
87
+ - Desired complex amplitude error below 3e-5 for each 0.15-amplitude carrier
88
+
89
+ These are bounded measurements, not a proof over every real frequency or an
90
+ arbitrary rapidly changing signal. DC and Nyquist have no usable Hilbert
91
+ quadrature. Near either edge, image rejection degrades; a near-DC rendered
92
+ counterexample intentionally asserts a substantial unwanted sideband.
93
+
94
+ There is **no input bandpass or output anti-alias filter**. The caller must keep
95
+ every desired `f + shiftHz` strictly between 0 and sampleRate/2 and leave room for
96
+ modulation sidebands. A translation through DC reflects with conjugated phase;
97
+ a translation through Nyquist aliases back into the band. The signed shift clamp
98
+ does not enforce either guard for unknown input content. Tests deliberately
99
+ demonstrate the Nyquist folding case. This module cannot preserve harmonic ratios
100
+ as a pitch shifter would.
101
+
102
+ Background on finite-duration Hilbert transitions:
103
+ [Julius O. Smith, Hilbert Transform Design Example](https://www.dsprelated.com/freebooks/sasp/Hilbert_Transform_Design_Example.html).
104
+ The implementation and acceptance numbers above are specific to this module's
105
+ own Blackman kernel and tests.
106
+
107
+ ## Evidence and resource boundary
108
+
109
+ `tests/frequency-shifter.spec.ts` checks construction, direct impulse/convolution,
110
+ signed complex sidebands, immediate automation and clamps, zero-Hz retained
111
+ phase, delayed bypass, held/off-boundary resets, full normalized f32 amplitudes,
112
+ tiny signed controls, output headroom, aliases, and native snapshot continuation
113
+ at three offline rates. Expectations are independently calculated candidates,
114
+ never replacement golden audio.
115
+
116
+ `tests/frequency-shifter-packed.test.mjs` installs the actual tarball in an
117
+ isolated locked consumer, strict-checks the public entry, repeats three-rate
118
+ convolution/complex-sideband/state tests and builds its Vite worklet. It retains
119
+ the tarball, consumer lock, JSON measurements and source hashes under the
120
+ candidate evidence manifest. Worklet compilation is not browser execution.
121
+
122
+ Storage is construction-fixed: 63 f64 history cells (504 bytes), three persistent
123
+ scalar state fields (cursor, validity and scaled phase), plus one transient f64
124
+ accumulator. The generated processor adds normal unworklet input/output/runtime
125
+ storage. No runtime allocation, FFT, asset loading, dependency change, oversampled
126
+ path or background scheduler is introduced.
127
+
128
+ The fresh isolated consumer measures compilation, actual generated WASM size,
129
+ memory, cold/startup 128-frame cost and 256 warm blocks including five input and
130
+ one output copies. Memory growth and nonfinite scrub counts are asserted zero.
131
+ Reported p50/p99/max and deadline exceedances are local Node diagnostics, not a
132
+ portable deadline or browser realtime guarantee. Browser execution, independent
133
+ review and exact-head integration CI remain distinct acceptance gates.
@@ -0,0 +1,22 @@
1
+ # Mobile listening room
2
+
3
+ This historical Envelope/LFO listening room is now a **test-only** fixture.
4
+ It is not included in the public website. `npm run build:consumer` and the
5
+ independent entry/audition tests still build its isolated packed consumer for
6
+ verification; the public `build:site` command does not call them.
7
+
8
+ The composition uses public `@denaudio/den/envelope`, `/lfo`, and `/oscillator` subpaths. It is one sine oscillator, one linear envelope, and pitch modulation via the existing LFO/depth function. Native unworklet AudioParams carry control changes. It is a module audition, not the full configurable MIDI instrument or delay FX. Oscillator dependency PR #18 and envelope/LFO PR #17 are merged. Delay/voice work is deliberately outside this first frontend slice.
9
+
10
+ Start creates and resumes a 48 kHz context in the user gesture. Release lowers the envelope gate; Stop fades the native master gain for 20 ms, disposes the node and closes the context. Pointer capture handles finger release/cancellation, keyboard Space/Enter supports the pad, and page hiding stops audio. A start cancelled during asynchronous worklet installation cannot reconnect its node. No microphone is requested.
11
+
12
+ Output gain defaults to 0.035 and the control caps it at 0.1. This limits digital amplitude, not device sound pressure. The page tells listeners to begin with low device volume. The waveform samples output after this gain and displays it enlarged four times. The page exposes exact composition source, current settings, candidate status and three listening checkpoints. No click or playback action constitutes listening approval.
13
+
14
+ ## Verification
15
+
16
+ `tests/audition.test.mjs` packs into an isolated consumer, typechecks public imports, production-builds both pages, renders the actual audition processor against an independent sine-times-linear-envelope formula at 48 kHz, and launches Chromium without an autoplay bypass. A 390×844 touch viewport verifies gesture start, output bounds, volume-zero silence, envelope-release silence, repeated Start/Stop, cancelled touch, startup cancellation, context closure and absence of page errors. Native analyser readings are checked independently of the UI status. Physical iOS/Android behavior still needs device listening; touch emulation does not establish that coverage.
17
+
18
+ Candidate WAV, source/audio hashes, settings, browser results and mobile screenshots are written under `artifacts/audition`. Existing browser validation remains 48 kHz; known 44.1/96 kHz baked-rate rejections remain expected entry-gate checks. No expected audio is changed and no approved golden is introduced.
19
+
20
+ The combined numerical suite runs with one worker to avoid CPU contention while compiling/rendering WASM. Default and two-worker runs intermittently exceeded the unchanged LFO test’s five-second timeout; sample assertions and timeout values are unchanged.
21
+
22
+ The initial audible preview exposed a sustained playback deadline failure that numerical and lifecycle checks missed. See [the real-time investigation and regression](realtime-audition.md). The audition now materializes three subgraph boundaries using transient unworklet state; the test command includes a separate real-time capture/deadline stage.
@@ -0,0 +1,53 @@
1
+ # Master stop scheduling: argument and clock observations
2
+
3
+ The instrument's existing stop path captures `AudioContext.currentTime` once,
4
+ cancels scheduled values at that time, sets the current master level at the same
5
+ time, and requests a zero endpoint 20 ms later. This change leaves that runtime
6
+ code and every audio/gain threshold unchanged.
7
+
8
+ The previous browser assertion compared the endpoint with a second clock read
9
+ inside its recorder. [The Web Audio clock definition](https://www.w3.org/TR/webaudio-1.0/#dom-baseaudiocontext-currenttime)
10
+ allows the rendering clock to advance between those reads. A correct 20 ms
11
+ schedule can therefore fail the old assertion. Conversely, an incorrect duration
12
+ can appear correct when a clock advance masks its error.
13
+
14
+ ## Retained observation
15
+
16
+ Post-main run [37399904290](https://github.com/yuichkun/den/actions/runs/37399904290)
17
+ on `6e947866f2be5fcf2f74dfc75ea8ebafc90b0258` failed the inherited assertion
18
+ `abs(rampEnd - sampledCurrentTime - .02) < 1e-9`. Full artifact `11385641596`
19
+ has SHA256 `669a4720f7852e9b48dd52add8623cc0f84428528bf817f5e851352624f286f4`.
20
+ The exact recorded argument/clock tuple was not available for comparison, so
21
+ that specific run remains unclassified. The source-based counterexamples below
22
+ do not prove whether its endpoint was still future or had actually become late.
23
+
24
+ ## Corrected invariant
25
+
26
+ The test records `cancelScheduledValues`, `setValueAtTime`,
27
+ `linearRampToValueAtTime` and `setTargetAtTime` for each individual GainNode.
28
+ Each completed stop must have, in order:
29
+
30
+ 1. A nonfuture cancellation anchor.
31
+ 2. A set-value call at exactly the same anchor, within the master gain range.
32
+ 3. A zero ramp endpoint exactly 20 ms after that anchor, retaining the existing
33
+ 1e-9-second interval tolerance.
34
+ 4. An endpoint that is still strictly future at the independently sampled ramp
35
+ invocation clock. Correct relative arguments do not excuse a due/past target.
36
+
37
+ Every recorded master must finish its sequence. Calls from different nodes cannot
38
+ supply each other's anchors. Target smoothing remains exactly 15 ms and its gain
39
+ range is unchanged. Raw calls, scheduled duration, clock advance and remaining
40
+ time are retained; failures print the complete offending sequence.
41
+
42
+ The small source-extracted Node tests execute the actual stop function and its
43
+ actual recorder with controlled clock reads. One 128/48000-second advance makes
44
+ the old oracle reject a correct schedule even though 17.333 ms remains. Incorrect
45
+ 22.6667 ms and 41.3333 ms intervals can pass that old oracle when matching clock
46
+ advances mask them. The corrected invariant rejects those, missing/reordered or
47
+ cross-node calls, wrong endpoints/values, future anchors, and due/past endpoints.
48
+
49
+ These are scheduling-contract proofs. The unchanged native offline automation
50
+ test still checks the actual 20 ms linear fade, final zero and sample-step bounds.
51
+ The actual browser run separately checks routing, output levels and cleanup.
52
+ Neither valid API arguments nor these functional checks certify a hardware fade,
53
+ general scheduling latency or runtime acceptance. Runtime remains NOT_CLEARED.
@@ -0,0 +1,79 @@
1
+ # Initial candidate acceptance record
2
+
3
+ Scope: GEN-608's initial shared DSP, one MIDI instrument engine, three instrument
4
+ settings, one Delay FX engine and two effect settings. No new DSP, framework,
5
+ upstream unworklet change or registry publication is included in this integration.
6
+
7
+ ## Current deployment status
8
+
9
+ This record retains the initial candidate's historical integration/listening
10
+ history. On 2026-10-06, the previous public UI was removed before rebuilding.
11
+ `build:site` now emits only a rebuilding notice; all historical players and
12
+ checks below are isolated test fixtures, not current public routes. See
13
+ [public site removal](deployment-status.md).
14
+
15
+ ## Decision boundary
16
+
17
+ This is an **initial usable CANDIDATE**. Source and behavior are based on
18
+ `196a1c3ef0e8eda9a007a2474c9d06f7f654acfa`. The final integration adds missing
19
+ public package subpaths, tests those imports, and makes the packed site build a
20
+ maintainable main-branch deployment while preserving the diagnostic pages.
21
+ Source under `src/`, the sound/FX values, dependencies and lockfiles are unchanged.
22
+ Independent review and exact-head CI are required before main merge. This file
23
+ does not claim the pending final merge or its CI has already completed.
24
+
25
+ The instrument runtime status remains **NOT_CLEARED**. Intermittent frame-clock
26
+ and output-underrun observations have not been attributed conclusively to den.
27
+ Per the owner's 2026-10-05 direction, den-specific reproducible defects warrant
28
+ investigation; open-ended upstream investigation is not a prerequisite for
29
+ continuing the initial candidate. This is not an all-device stability guarantee.
30
+ No test threshold is relaxed and no historical failure is converted into a pass.
31
+
32
+ ## Acceptance criteria and evidence
33
+
34
+ | Scope | Implementation / evidence | Boundary |
35
+ | --- | --- | --- |
36
+ | Shared Envelope / LFO / filter | Independent numerical/time/phase/reset/response tests; existing shared implementations are used by both engines where applicable | 44.1/48/96 kHz offline; browser 48 kHz only |
37
+ | Voice / oscillator / instrument (GEN-617) | Voice allocation, legato/retrigger, steals, MIDI note end, per-voice envelopes/modulation, bypass/reset and same-schema restoration; `instrument.spec.ts`, `voice-policy.spec.ts`, isolated packed instrument tests | Construction capacity is not a proven real-time product limit |
38
+ | Reusable public API | `filter`, `voice-policy`, `delay-readhead`, and `instrument-example` now join the existing public subpaths; isolated consumers typecheck and render public imports, including the replacement oscillator/filter | No physical `node_modules/.../dist` import escape remains in the fixtures |
39
+ | Three sounds (GEN-619) | One engine with plain Bass / Percussion / Pad construction settings and complete native AudioParam maps; numerical fixtures and packed dry phrase/stress/snapshot tests | All raw audio remains CANDIDATE |
40
+ | Two FX settings (GEN-621) | Same Delay engine with Chorus and Rhythmic delay settings; independent delay-time/modulation/feedback/tempo/tail and bypass/reset fixtures | Moving-head pitch/click behavior follows the documented contract |
41
+ | Integrated sound/FX use | 4×3 packed numerical and browser matrix, all 22 initial sound parameters, stopped-only selectors, release/reset, cancellation/restart/context disposal; source identity assertions preserve raw candidate sounds | Fixed-input headroom measurements do not cover arbitrary edits or every device |
42
+ | Historical deployment boundary | The initial integration combined identical packed packages with instrument, diagnostics and module audition routes; their verification now lives in isolated test fixtures | The current public UI is removed; existing Vercel access/header/project settings remain unchanged |
43
+ | Provenance | Per-run manifests connect source, dependencies, settings/parameters, MIDI/input, seed, rates, WAV/raw PCM, static waveform and verification | Human feedback and golden promotion are recorded separately |
44
+
45
+ The prior candidate's [exact-head CI 37279030893](https://github.com/yuichkun/den/actions/runs/37279030893)
46
+ passed 226 numerical, 10 packed and 2 realtime tests. That is prior-revision
47
+ machine evidence, not a substitute for this integration's exact-head run.
48
+ [Headroom evidence](sound-audition-headroom.md) records the 90-row matrix maximum
49
+ peak 0.368202865 (8.678 dB headroom) at Master 1 with zero nonfinite/clipped values.
50
+ The numerical assertions and full existing browser/lifecycle suite remain intact.
51
+
52
+ ## Candidate listening feedback received
53
+
54
+ - 2026-10-05 15:55 JST: the owner replied “よさそう” to the three-sound WAV review.
55
+ [Original feedback](https://escentier.slack.com/archives/C0C6LD2PH9S/p1791183335912499?thread_ts=1791107749.905829&cid=C0C6LD2PH9S)
56
+ - 2026-10-05 16:57 JST: the owner replied “問題なさそうありがとう” after the
57
+ integrated preview with sound switching and combined FX.
58
+ [Original feedback](https://escentier.slack.com/archives/C0C6LD2PH9S/p1791187049139809?thread_ts=1791107749.905829&cid=C0C6LD2PH9S)
59
+
60
+ These are positive candidate direction and device-use evaluations. Neither
61
+ request is awaiting a reply. They are not exact-source/audio-hash golden
62
+ approval, a general-release decision, or a claim of universal device stability.
63
+ No golden file is added or changed. Future sound changes require new evidence;
64
+ this integration does not silently transfer approval to different DSP/settings.
65
+
66
+ ## Retained failures and limits
67
+
68
+ The original candidate `334d2db` had a local packed frame-clock failure (8/9
69
+ passed) and normal native/full realtime underrun failures. Later CI passes do
70
+ not erase those observations. The [PR26 historical record](https://github.com/yuichkun/den/pull/26)
71
+ retains the failed logs/archive hash and exact later runs. The
72
+ [instrument investigation](instrument-performance.md) and
73
+ [realtime audition record](realtime-audition.md) retain controls and limitations.
74
+ PR28 also retains local browser/timeout restrictions and the cancelled short-job
75
+ run separately from its eventual successful CI. Do not relabel those runs.
76
+
77
+ Generated test manifests describe that run's mechanical/physical-listening
78
+ limits; they do not negate the separately linked owner feedback. Hardware
79
+ loopback, broad device coverage and approved golden baselines are not claimed.