Skip to content

Commit d5de163

Browse files
committed
Integrate updated automation-events
1 parent 2b58749 commit d5de163

3 files changed

Lines changed: 22 additions & 81 deletions

File tree

package-lock.json

Lines changed: 8 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232
"dsp"
3333
],
3434
"dependencies": {
35-
"audio-effect": "^1.0.0",
3635
"audio-buffer": "^7.3.0",
3736
"audio-decode": "^3.1.2",
37+
"audio-effect": "^1.0.0",
3838
"audio-speaker": "^2.0.5",
39-
"automation-events": "^7.1.15",
39+
"automation-events": "^7.1.17",
4040
"digital-filter": "^2.3.0",
4141
"fourier-transform": "^2.2.0",
4242
"pcm-convert": "^3.1.1",

src/AudioParam.js

Lines changed: 12 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -158,36 +158,14 @@ class AudioParam extends DspObject {
158158
throw DOMErr('setValueCurveAtTime overlaps an existing event', 'NotSupportedError')
159159
}
160160
}
161+
// Store as regular Array to ensure float64 interpolation during truncation
162+
// (Float32Array.slice preserves type, causing float32 precision loss)
163+
if (ArrayBuffer.isView(values)) values = Array.from(values)
161164
this.#automationEventList.add(createSetValueCurveAutomationEvent(values, startTime, duration))
162165
this.#paramVersion++
163166
return this
164167
}
165168

166-
// Wraps AutomationEventList.getValue to fix exponentialRamp with opposite-sign
167-
// or zero start values. The spec says: if v0 and v1 have opposite signs or v0
168-
// is zero, v(t) = v0 for T0 <= t < T1. The library returns 0 in this case.
169-
#getValue(time) {
170-
let val = this.#automationEventList.getValue(time)
171-
if (val === 0) {
172-
let events = this.#automationEventList._automationEvents
173-
for (let i = 0; i < events.length; i++) {
174-
let e = events[i]
175-
if (e.type === 'exponentialRampToValue' && time < e.endTime) {
176-
// Find the previous event's end value (the ramp's start value)
177-
let prev = events[i - 1]
178-
let v0 = prev === undefined ? this.#defaultValue
179-
: prev.type === 'setValueCurve' ? prev.values[prev.values.length - 1]
180-
: prev.value
181-
// Spec: opposite signs or v0 == 0 → hold v0
182-
if ((v0 > 0 && e.value < 0) || (v0 < 0 && e.value > 0) || v0 === 0)
183-
return v0
184-
break
185-
}
186-
}
187-
}
188-
return val
189-
}
190-
191169
_tick() {
192170
super._tick()
193171
this._dsp(this._outBuf)
@@ -213,25 +191,25 @@ class AudioParam extends DspObject {
213191
let inputBuf = this._input._tick()
214192
let ch0 = inputBuf.getChannelData(0)
215193
for (let i = 0; i < BLOCK_SIZE; i++)
216-
array[i] = this.#getValue((f0 + i) / sr) + ch0[i]
194+
array[i] = this.#automationEventList.getValue((f0 + i) / sr) + ch0[i]
217195
// NaN can enter from input — flush to default
218196
let def = this.#defaultValue
219197
for (let i = 0; i < BLOCK_SIZE; i++) if (isNaN(array[i])) array[i] = def
220198
} else {
221-
let v0 = this.#getValue(f0 / sr)
222-
let v1 = this.#getValue((f0 + BLOCK_SIZE - 1) / sr)
199+
let v0 = this.#automationEventList.getValue(f0 / sr)
200+
let v1 = this.#automationEventList.getValue((f0 + BLOCK_SIZE - 1) / sr)
223201
if (v0 === v1) {
224202
array.fill(v0)
225203
this.#cachedVersion = this.#paramVersion
226204
this.#cachedValue = v0
227205
} else {
228206
this.#cachedVersion = -1
229207
for (let i = 0; i < BLOCK_SIZE; i++)
230-
array[i] = this.#getValue((f0 + i) / sr)
208+
array[i] = this.#automationEventList.getValue((f0 + i) / sr)
231209
}
232210
}
233211
} else {
234-
let val = this.#getValue(f0 / sr)
212+
let val = this.#automationEventList.getValue(f0 / sr)
235213
if (hasInput) {
236214
let inputBuf = this._input._tick()
237215
val += inputBuf.getChannelData(0)[0]
@@ -256,54 +234,13 @@ class AudioParam extends DspObject {
256234
cancelAndHoldAtTime(cancelTime) {
257235
_assertTime(cancelTime)
258236

259-
// Snapshot setValueCurve events that span cancelTime so we can fix
260-
// truncation precision after the library processes the cancelAndHold.
261-
let events = this.#automationEventList._automationEvents
262-
let origCurve = null
263-
for (let e of events) {
264-
if (e.type === 'setValueCurve' &&
265-
e.startTime < cancelTime &&
266-
e.startTime + e.duration > cancelTime) {
267-
origCurve = {
268-
values: Array.from(e.values), // copy as float64
269-
startTime: e.startTime,
270-
duration: e.duration
271-
}
272-
break
273-
}
274-
}
275-
276237
this.#automationEventList.add(createCancelAndHoldAutomationEvent(cancelTime))
277238
this.#paramVersion++
278239

279-
events = this.#automationEventList._automationEvents
280-
281-
// Fix truncated setValueCurve precision: the library resamples curve values
282-
// through Float32Array which loses precision. Recompute in float64.
283-
if (origCurve) {
284-
let last = events[events.length - 1]
285-
if (last && last.type === 'setValueCurve' &&
286-
last.startTime === origCurve.startTime) {
287-
let newDuration = cancelTime - origCurve.startTime
288-
let ratio = (origCurve.values.length - 1) / origCurve.duration
289-
let length = Math.max(2, 1 + Math.ceil(newDuration * ratio))
290-
let fraction = (newDuration / (length - 1)) * ratio
291-
let values = origCurve.values.slice(0, length)
292-
if (fraction < 1) {
293-
for (let i = 1; i < length; i++) {
294-
let factor = (fraction * i) % 1
295-
values[i] = origCurve.values[i - 1] * (1 - factor) + origCurve.values[i] * factor
296-
}
297-
}
298-
last.values = values
299-
last.duration = newDuration
300-
}
301-
}
302-
303-
// The library stores held values (truncated ramp endpoints, setValue for
304-
// setTarget) in float64. But the Web Audio spec outputs through Float32Array,
305-
// so subsequent automations should start from the float32-rounded held value.
306-
// Apply Math.fround to the last event's value to match spec precision.
240+
// The library stores held values in float64, but Web Audio spec outputs
241+
// through Float32Array — subsequent automations must start from the
242+
// float32-rounded held value. Apply Math.fround to match spec precision.
243+
let events = this.#automationEventList._automationEvents
307244
let last = events[events.length - 1]
308245
if (last) {
309246
if ((last.type === 'linearRampToValue' || last.type === 'exponentialRampToValue')

0 commit comments

Comments
 (0)