Description
setTargetAtTime silently drops events when the startTime equals the previous event's endTime. This is because AudioParam.cpp line 150 uses <= instead of <:
void AudioParam::setTargetAtTime(float target, double startTime, double timeConstant) {
auto event = [target, startTime, timeConstant](AudioParam ¶m) {
if (startTime <= param.getQueueEndTime()) { // ← should be <
return;
}
Since setTargetAtTime sets its own endTime = startTime (the event has "infinite" conceptual duration), the getQueueEndTime() after a linearRampToValueAtTime ending at time T will be T. A subsequent setTargetAtTime starting at exactly T is then dropped because T <= T is true.
This breaks standard ADSR envelope patterns where setTargetAtTime (decay) is scheduled at exactly the moment linearRampToValueAtTime (attack) ends — a pattern used in every Web Audio ADSR tutorial.
Steps to reproduce
- Clone the repro repo:
git clone https://github.com/HamptonMakes/rnaa-settargetattime-bug
cd rnaa-settargetattime-bug && npm install && npx expo run:ios
- Press "Play broken (no EPS)" — flat beep, no decay
- Press "Play fixed (with EPS)" — tone decays naturally
The only difference is adding 0.0001 to the setTargetAtTime start time.
Or, in code:
const ac = new AudioContext();
const osc = ac.createOscillator();
const gain = ac.createGain();
osc.connect(gain);
gain.connect(ac.destination);
const now = ac.currentTime;
const attackEnd = now + 0.02;
gain.gain.setValueAtTime(0.0001, now);
gain.gain.linearRampToValueAtTime(0.3, attackEnd);
// ↓ SILENTLY DROPPED — attackEnd <= attackEnd is true
gain.gain.setTargetAtTime(0.03, attackEnd, 0.1);
osc.start(now);
osc.stop(now + 2);
// Expected: ramp up then exponential decay
// Actual: ramp up, stays at 0.3 forever (flat beep)
Snack or a link to a repository
https://github.com/HamptonMakes/rnaa-settargetattime-bug
React Native Audio API version
0.11.7
React Native version
0.81.5
Platforms
iOS
JavaScript runtime
Hermes
Workflow
Expo Dev Client
Architecture
Fabric (New Architecture)
Build type
Debug app & dev bundle
Device
iOS simulator
Device model
iPhone 16 Pro (iOS 18.6)
Acknowledgements
Yes
Description
setTargetAtTimesilently drops events when thestartTimeequals the previous event'sendTime. This is becauseAudioParam.cppline 150 uses<=instead of<:Since
setTargetAtTimesets its ownendTime = startTime(the event has "infinite" conceptual duration), thegetQueueEndTime()after alinearRampToValueAtTimeending at time T will be T. A subsequentsetTargetAtTimestarting at exactly T is then dropped becauseT <= Tis true.This breaks standard ADSR envelope patterns where
setTargetAtTime(decay) is scheduled at exactly the momentlinearRampToValueAtTime(attack) ends — a pattern used in every Web Audio ADSR tutorial.Steps to reproduce
git clone https://github.com/HamptonMakes/rnaa-settargetattime-bugcd rnaa-settargetattime-bug && npm install && npx expo run:iosThe only difference is adding
0.0001to thesetTargetAtTimestart time.Or, in code:
Snack or a link to a repository
https://github.com/HamptonMakes/rnaa-settargetattime-bug
React Native Audio API version
0.11.7
React Native version
0.81.5
Platforms
iOS
JavaScript runtime
Hermes
Workflow
Expo Dev Client
Architecture
Fabric (New Architecture)
Build type
Debug app & dev bundle
Device
iOS simulator
Device model
iPhone 16 Pro (iOS 18.6)
Acknowledgements
Yes