Skip to content

Improve fade in/out API in SoundGenerator. #191

@pixelzoom

Description

@pixelzoom

SoundGenerator's current fade in/out support is awkward. You need to compute a proper timeConstant based on your desired fade time, use setOutputLevel to perform the fade (not at all obvious), and call start/stop yourself at the proper time. It results in boilerplate code like this:

const FADE_IN_TIME = 0.5; // seconds
const FADE_OUT_TIME = 0.25; // seconds

// fade in
soundClip.play();
soundClip.setOutputLevel( 1, FADE_IN_TIME / 3 );

// fade out
soundClip.setOutputLevel( 0, FADE_OUT_TIME / 3 );
soundClip.stop( FADE_OUT_TIME );

WebAudio timeConstant seems to be generally misunderstood, and the documentation is complicated. I don't see any consistency in how timeConstant is computed in PhET code, which seems like another problem. Above I'm using FADE_OUT_TIME / 3 because https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/setTargetAtTime#timeconstant says: "Depending on your use case, getting 95% toward the target value may already be enough; in that case, you could set timeConstant to one third of the desired duration." This seems to work nicely, but I don't see it being used anywhere in PhET code.

So if we must expose timeConstant in the SoundGenerator API, then some assistance in computing timeConstant would help to provide consistency across PhET code, and not make every developer have to deal directly with this confusing part of the WebAudio API. For example, here's what I added to FELUtils.ts:

  /**
   * SoundClip.setOutputLevel uses WebAudio setTargetAtTime to set output level, with optional fade. The timeConstant
   * argument to setTargetAtTime is NOT the fade time, it's an exponential approach to the target output level.
   * Doc for setTargetAtTime at https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/setTargetAtTime#timeconstant
   * says: "Depending on your use case, getting 95% toward the target value may already be enough; in that case, you
   * could set timeConstant to one third of the desired duration."  So that's the basis for this implementation.
   */
  secondsToTimeConstant( seconds: number ): number {
    return seconds / 3;
  }

I don't know who to assign this to, so I'll just mention @jbphet and @zepumph, since they've been responsive to sound issue.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions