Skip to content

Commit

Permalink
improve sound generation, see #185
Browse files Browse the repository at this point in the history
  • Loading branch information
jbphet committed Oct 4, 2022
1 parent 439c953 commit 4fe46e4
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 24 deletions.
1 change: 1 addition & 0 deletions js/common/view/FluxMeterNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ class FluxMeterNode extends Node {
public reset(): void {
this.wasDraggedProperty.reset();
this.zoomFactor.reset();
this.soundGenerator.reset();
}
}

Expand Down
72 changes: 48 additions & 24 deletions js/common/view/FluxMeterSoundGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ import greenhouseEffect from '../../greenhouseEffect.js';
// constants
const DEFAULT_OUTPUT_LEVEL = 0.22;

// amount of time after a change before starting the fade out, in seconds
const PRE_FADE_TIME = 1;
// amount of time before starting to fade where flux change is below the threshold, in seconds
const PRE_FADE_TIME = 0.5;

// amount of time to fade from full volume to zero, in seconds
const FADE_TIME = 1;
// rate at which the sounds increase in volume, in proportion per second
const FADE_IN_RATE = 5;

// rate at which sounds decrease in volume, in proportion per second
const FADE_OUT_RATE = 2;

const FADE_OUT_TIME = 1 / FADE_OUT_RATE;

// the rate of flux change considered necessary to produce sound, empirically determined
const FLUX_CHANGE_RATE_THRESHOLD = 300000;
Expand Down Expand Up @@ -60,7 +65,10 @@ class FluxMeterSoundGenerator extends SoundGenerator {
this.irPreviousFluxUp = irFluxUpProperty.value;

// Create the sound clip user for IR flux in the upward direction.
this.irUpFluxSoundClip = new SoundClip( irFluxUp_mp3, { rateChangesAffectPlayingSounds: false } );
this.irUpFluxSoundClip = new SoundClip( irFluxUp_mp3, {
initialOutputLevel: 0,
loop: true
} );
this.irUpFluxSoundClip.connect( this.masterGainNode );

// Define the class-specific dispose function.
Expand All @@ -78,40 +86,56 @@ class FluxMeterSoundGenerator extends SoundGenerator {

const irFluxUpChangeRateThisStep = Math.abs( this.irPreviousFluxUp - this.irFluxUpProperty.value ) / dt;

// Adjust the countdown timer based on the rate of flux change.
if ( irFluxUpChangeRateThisStep > FLUX_CHANGE_RATE_THRESHOLD ) {

// The flux is changing at a rate high enough that the corresponding sound should be produced at full volume.
if ( !this.irUpFluxSoundClip.isPlaying ) {
this.irUpFluxSoundClip.play();
}
this.irUpFluxSoundClip.outputLevel = 1;
this.irUpFluxChangedCountdownTimer = PRE_FADE_TIME + FADE_TIME;
this.irUpFluxChangedCountdownTimer = PRE_FADE_TIME + FADE_OUT_TIME;
}
else {

// The change during this step is below the threshold, so fade the sound.
this.irUpFluxChangedCountdownTimer = Math.max( this.irUpFluxChangedCountdownTimer - dt, 0 );
if ( this.irUpFluxChangedCountdownTimer === 0 ) {
}

// Adjust the output level of the IR flux up sound based on the corresponding countdown timer and the current output
// level.
let irUpFluxSoundOutputLevel = this.irUpFluxSoundClip.outputLevel;
if ( this.irUpFluxChangedCountdownTimer > FADE_OUT_TIME ) {

// It's time to stop this sound from playing.
this.irUpFluxSoundClip.outputLevel = 0;
this.irUpFluxSoundClip.stop();
}
else if ( this.irUpFluxChangedCountdownTimer < PRE_FADE_TIME ) {
// Move towards full volume if not already there.
irUpFluxSoundOutputLevel = Math.min( irUpFluxSoundOutputLevel + FADE_IN_RATE * dt, 1 );
}
else if ( this.irUpFluxChangedCountdownTimer > 0 ) {
irUpFluxSoundOutputLevel = Math.max( irUpFluxSoundOutputLevel - FADE_OUT_RATE * dt, 0 );
}
else {
irUpFluxSoundOutputLevel = Math.max( irUpFluxSoundOutputLevel - 0.1 * dt, 0 );
}

// Based on the countdown timer value, this sound is fading, so set the output level appropriately.
this.irUpFluxSoundClip.outputLevel = Math.max( this.irUpFluxChangedCountdownTimer - PRE_FADE_TIME / FADE_TIME, 0 );
}
// Start or stop the sound clip if appropriate.
if ( irUpFluxSoundOutputLevel > 0 && !this.irUpFluxSoundClip.isPlaying ) {
this.irUpFluxSoundClip.play();
}
else if ( irUpFluxSoundOutputLevel === 0 && this.irUpFluxSoundClip.isPlaying ) {
this.irUpFluxSoundClip.stop( 0.1 );
}

// Set the output level.
this.irUpFluxSoundClip.setOutputLevel( irUpFluxSoundOutputLevel );

// Set the playback rate for the IR up sound. This happens regardless of whether it is playing.
const playbackRate = 1 + Math.min( this.irFluxUpProperty.value / MAX_EXPECTED_IR_FLUX, 1 ) * 2;
const playbackRate = 1 + Math.min( this.irFluxUpProperty.value / MAX_EXPECTED_IR_FLUX, 1 ) * 4;
this.irUpFluxSoundClip.setPlaybackRate( playbackRate );

// Update the previous flux values for the next step.
this.irPreviousFluxUp = this.irFluxUpProperty.value;
}

public reset(): void {
this.irUpFluxChangedCountdownTimer = 0;
this.irUpFluxSoundClip.outputLevel = 0;
this.irUpFluxSoundClip.stop( 0.1 );
this.irUpFluxSoundClip.setPlaybackRate( 1 );
this.irPreviousFluxUp = 0;
}

public override dispose(): void {
this.disposeFluxMeterSoundGenerator();
super.dispose();
Expand Down

0 comments on commit 4fe46e4

Please sign in to comment.