diff --git a/js/common/view/RatioAndProportionScreenView.js b/js/common/view/RatioAndProportionScreenView.js index 33f6fff7..d40baef2 100644 --- a/js/common/view/RatioAndProportionScreenView.js +++ b/js/common/view/RatioAndProportionScreenView.js @@ -89,6 +89,16 @@ class RatioAndProportionScreenView extends ScreenView { // @protected this.tickMarkViewProperty = tickMarkViewProperty; + // @private - SoundGenerators that sonify different aspects of the model + this.inProportionSoundGenerator = new InProportionSoundGenerator( model ); + this.movingInProportionSoundGenerator = new MovingInProportionSoundGenerator( model ); + this.staccatoFrequencySoundGenerator = new StaccatoFrequencySoundGenerator( model.ratioFitnessProperty, model.fitnessRange, + model.inProportion.bind( model ) ); + + soundManager.addSoundGenerator( this.staccatoFrequencySoundGenerator ); + soundManager.addSoundGenerator( this.inProportionSoundGenerator ); + soundManager.addSoundGenerator( this.movingInProportionSoundGenerator ); + // by default, the keyboard step size should be half of one default tick mark width. See https://github.com/phetsims/ratio-and-proportion/issues/85 const keyboardStep = 1 / 2 / options.tickMarkRangeProperty.value; @@ -114,7 +124,7 @@ class RatioAndProportionScreenView extends ScreenView { tickMarksAndLabelsColorProperty, keyboardStep, model.lockRatioProperty, - playUISoundsProperty, { + playUISoundsProperty, this.inProportionSoundGenerator, { accessibleName: ratioAndProportionStrings.a11y.leftHand, a11yDependencies: a11yDependencies, isRight: false // this way we get a left hand @@ -135,7 +145,7 @@ class RatioAndProportionScreenView extends ScreenView { tickMarksAndLabelsColorProperty, keyboardStep, model.lockRatioProperty, - playUISoundsProperty, { + playUISoundsProperty, this.inProportionSoundGenerator, { accessibleName: ratioAndProportionStrings.a11y.rightHand, a11yDependencies: a11yDependencies, helpText: ratioAndProportionStrings.a11y.rightHandHelpText @@ -188,24 +198,16 @@ class RatioAndProportionScreenView extends ScreenView { // @private TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89 // this.markerInput = new ProportionMarkerInput( model ); - const soundGeneratorOptions = { - enableControlProperties: [ DerivedProperty.or( [ - this.leftRatioHalf.isBeingInteractedWithProperty, - this.rightRatioHalf.isBeingInteractedWithProperty, - // this.markerInput.isBeingInteractedWithProperty, // TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89 - ratioInteractionListener.isBeingInteractedWithProperty - ] ) ] - }; - - // @private - SoundGenerators that sonify different aspects of the model - this.inProportionSoundGenerator = new InProportionSoundGenerator( model, soundGeneratorOptions ); - this.movingInProportionSoundGenerator = new MovingInProportionSoundGenerator( model, soundGeneratorOptions ); - this.staccatoFrequencySoundGenerator = new StaccatoFrequencySoundGenerator( model.ratioFitnessProperty, model.fitnessRange, - model.inProportion.bind( model ), soundGeneratorOptions ); + const soundGeneratorEnabledProperty = DerivedProperty.or( [ + this.leftRatioHalf.isBeingInteractedWithProperty, + this.rightRatioHalf.isBeingInteractedWithProperty, + // this.markerInput.isBeingInteractedWithProperty, // TODO: add support for mechamarker input again https://github.com/phetsims/ratio-and-proportion/issues/89 + ratioInteractionListener.isBeingInteractedWithProperty + ] ); - soundManager.addSoundGenerator( this.staccatoFrequencySoundGenerator ); - soundManager.addSoundGenerator( this.inProportionSoundGenerator ); - soundManager.addSoundGenerator( this.movingInProportionSoundGenerator ); + this.inProportionSoundGenerator.addEnableControlProperty( soundGeneratorEnabledProperty ); + this.movingInProportionSoundGenerator.addEnableControlProperty( soundGeneratorEnabledProperty ); + this.staccatoFrequencySoundGenerator.addEnableControlProperty( soundGeneratorEnabledProperty ); // these dimensions are just temporary, and will be recomputed below in the layout function const labelsNode = new RAPTickMarkLabelsNode( tickMarkViewProperty, options.tickMarkRangeProperty, 1000, tickMarksAndLabelsColorProperty ); diff --git a/js/common/view/RatioHalf.js b/js/common/view/RatioHalf.js index 6705e2fc..baa75056 100644 --- a/js/common/view/RatioHalf.js +++ b/js/common/view/RatioHalf.js @@ -73,10 +73,13 @@ class RatioHalf extends Rectangle { * @param {number} keyboardStep * @param {BooleanProperty} horizontalMovementAllowedProperty * @param {BooleanProperty} playUISoundsProperty + * @param {InProportionSoundGenerator} inProportionSoundGenerator * @param {Object} [options] */ constructor( valueProperty, valueRange, enabledValueRangeProperty, firstInteractionProperty, bounds, tickMarkViewProperty, - tickMarkRangeProperty, ratioDescriber, handPositionsDescriber, colorProperty, keyboardStep, horizontalMovementAllowedProperty, playUISoundsProperty, options ) { + tickMarkRangeProperty, ratioDescriber, handPositionsDescriber, colorProperty, keyboardStep, + horizontalMovementAllowedProperty, playUISoundsProperty, inProportionSoundGenerator, + options ) { options = merge( { isRight: true, // right ratio or the left ratio @@ -211,6 +214,8 @@ class RatioHalf extends Rectangle { } commonGrabSoundClip.play(); firstInteractionProperty.value = false; + + inProportionSoundGenerator.setJumpingOverProportionShouldTriggerSound( true ); }, drag: () => { this.isBeingInteractedWithProperty.value = true; @@ -252,6 +257,7 @@ class RatioHalf extends Rectangle { commonReleaseSoundClip.play(); this.isBeingInteractedWithProperty.value = false; this.alertManager.alertRatioChange(); + inProportionSoundGenerator.setJumpingOverProportionShouldTriggerSound( false ); } } ); diff --git a/js/common/view/sound/InProportionSoundGenerator.js b/js/common/view/sound/InProportionSoundGenerator.js index 5c830e4e..79300c40 100644 --- a/js/common/view/sound/InProportionSoundGenerator.js +++ b/js/common/view/sound/InProportionSoundGenerator.js @@ -41,6 +41,12 @@ class InProportionSoundGenerator extends SoundClip { // @private - True when, in the previous step, the current ratio (calculated from leftValue/rightValue) is larger than // the target ratio. this.currentRatioWasLargerThanTarget = this.calculateCurrentRatioLargerThanTarget(); + + // @private - in certain cases ratio hand positions can move so quickly "through" the in-proportion range that an + // actual "in proportion" value is never set. When this boolean is true, then this SoundGenerator will note when + // this "jump over in proportion" occurs, and still play the sound. This is useful for mouse interaction, but not + // so much for keyboard interaction. See https://github.com/phetsims/ratio-and-proportion/issues/162 + this.jumpingOverShouldSound = false; } /** @@ -51,6 +57,25 @@ class InProportionSoundGenerator extends SoundClip { return this.currentRatioProperty.value > this.model.targetRatioProperty.value; } + /** + * When true, the InProportionSoundGenerator will play when the ratio "jumps" over in proportion in two consecutive + * values of the current ratio. + * @public + * @param {boolean} jumpingOverShouldSound + */ + setJumpingOverProportionShouldTriggerSound( jumpingOverShouldSound ) { + this.jumpingOverShouldSound = jumpingOverShouldSound; + } + + /** + * True when the ratio jumped over being in proportion, but it should still sound that it was in proportion + * @private + * @returns {boolean} + */ + jumpedOverInProportionAndShouldSound() { + return this.jumpingOverShouldSound && this.calculateCurrentRatioLargerThanTarget() !== this.currentRatioWasLargerThanTarget; + } + /** * Step this sound generator, used for fading out the sound in the absence change. * @param {number} dt @@ -61,12 +86,11 @@ class InProportionSoundGenerator extends SoundClip { const isInRatio = this.model.inProportion(); - const currentRatioIsLargerThanTarget = this.calculateCurrentRatioLargerThanTarget(); // Only use hysteresis when both hands are moving. const hysteresisThreshold = this.model.movingInDirection() ? RatioAndProportionQueryParameters.hysteresisThreshold : 0; - if ( !this.playedSuccessYet && ( isInRatio || currentRatioIsLargerThanTarget !== this.currentRatioWasLargerThanTarget ) ) { + if ( !this.playedSuccessYet && ( isInRatio || this.jumpedOverInProportionAndShouldSound() ) ) { this.setOutputLevel( SUCCESS_OUTPUT_LEVEL, 0 ); this.play(); this.playedSuccessYet = true; @@ -82,7 +106,8 @@ class InProportionSoundGenerator extends SoundClip { this.setOutputLevel( SILENT_LEVEL, .1 ); } - this.currentRatioWasLargerThanTarget = currentRatioIsLargerThanTarget; + // for testing during next step() + this.currentRatioWasLargerThanTarget = this.calculateCurrentRatioLargerThanTarget(); } /**