From 1ba7ad0218474547aded1668f06b9a29559a0364 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 17 Mar 2017 17:23:53 -0400 Subject: [PATCH 1/4] =?UTF-8?q?Clamp=20range=20for=20=E2=80=9Cplay=20note?= =?UTF-8?q?=E2=80=9D=20input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clamp to 36-96, which is C2-C7. This is a temporary fix to prevent errors, until we have a new instrument player implementation, which may have a different range. --- src/blocks/scratch3_sound.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 76c03f6fd..0a5476310 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -33,6 +33,12 @@ Scratch3SoundBlocks.DEFAULT_SOUND_STATE = { } }; +/** + * The minimum and maximum MIDI note numbers, for clamping the input to play note. + * @type {{min: number, max: number}} + */ +Scratch3SoundBlocks.MIDI_NOTE_RANGE = {min: 36, max: 96}; // C2 to C7 + /** * @param {Target} target - collect sound state for this target. * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary. @@ -132,6 +138,7 @@ Scratch3SoundBlocks.prototype.stopAllSounds = function (args, util) { Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { var note = Cast.toNumber(args.NOTE); + note = MathUtil.clamp(note, Scratch3SoundBlocks.MIDI_NOTE_RANGE.min, Scratch3SoundBlocks.MIDI_NOTE_RANGE.max); var beats = Cast.toNumber(args.BEATS); var soundState = this._getSoundState(util.target); var inst = soundState.currentInstrument; From f2ac36585931e0f9cc6621bed946af3aef1237f5 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 17 Mar 2017 17:29:23 -0400 Subject: [PATCH 2/4] Clamp beat durations for play note, play drum and rest --- src/blocks/scratch3_sound.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 0a5476310..6bbc9e6a8 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -39,6 +39,13 @@ Scratch3SoundBlocks.DEFAULT_SOUND_STATE = { */ Scratch3SoundBlocks.MIDI_NOTE_RANGE = {min: 36, max: 96}; // C2 to C7 +/** + * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest. + * 100 beats at the default tempo of 60bpm is 100 seconds. + * @type {{min: number, max: number}} + */ +Scratch3SoundBlocks.BEAT_RANGE = {min: 0, max: 100}; + /** * @param {Target} target - collect sound state for this target. * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary. @@ -140,6 +147,7 @@ Scratch3SoundBlocks.prototype.playNoteForBeats = function (args, util) { var note = Cast.toNumber(args.NOTE); note = MathUtil.clamp(note, Scratch3SoundBlocks.MIDI_NOTE_RANGE.min, Scratch3SoundBlocks.MIDI_NOTE_RANGE.max); var beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); var soundState = this._getSoundState(util.target); var inst = soundState.currentInstrument; var vol = soundState.volume; @@ -153,16 +161,22 @@ Scratch3SoundBlocks.prototype.playDrumForBeats = function (args, util) { if (typeof this.runtime.audioEngine === 'undefined') return; drum = MathUtil.wrapClamp(drum, 0, this.runtime.audioEngine.numDrums); var beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); if (util.target.audioPlayer === null) return; return util.target.audioPlayer.playDrumForBeats(drum, beats); }; Scratch3SoundBlocks.prototype.restForBeats = function (args) { var beats = Cast.toNumber(args.BEATS); + beats = this._clampBeats(beats); if (typeof this.runtime.audioEngine === 'undefined') return; return this.runtime.audioEngine.waitForBeats(beats); }; +Scratch3SoundBlocks.prototype._clampBeats = function (beats) { + return MathUtil.clamp(beats, Scratch3SoundBlocks.BEAT_RANGE.min, Scratch3SoundBlocks.BEAT_RANGE.max); +}; + Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { var soundState = this._getSoundState(util.target); var instNum = Cast.toNumber(args.INSTRUMENT); From c9338f37ede057d935f11c7021c22c5011682d3f Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Fri, 17 Mar 2017 18:13:33 -0400 Subject: [PATCH 3/4] Clamp tempo range The function in AudioEngine for change tempo can now be removed --- src/blocks/scratch3_sound.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 6bbc9e6a8..696ece4a8 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -46,6 +46,11 @@ Scratch3SoundBlocks.MIDI_NOTE_RANGE = {min: 36, max: 96}; // C2 to C7 */ Scratch3SoundBlocks.BEAT_RANGE = {min: 0, max: 100}; + /** The minimum and maximum tempo values, in bpm. + * @type {{min: number, max: number}} + */ +Scratch3SoundBlocks.TEMPO_RANGE = {min: 20, max: 500}; + /** * @param {Target} target - collect sound state for this target. * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary. @@ -246,15 +251,21 @@ Scratch3SoundBlocks.prototype.getVolume = function (args, util) { }; Scratch3SoundBlocks.prototype.setTempo = function (args) { - var value = Cast.toNumber(args.TEMPO); - if (typeof this.runtime.audioEngine === 'undefined') return; - this.runtime.audioEngine.setTempo(value); + var tempo = Cast.toNumber(args.TEMPO); + this._updateTempo(tempo); }; Scratch3SoundBlocks.prototype.changeTempo = function (args) { - var value = Cast.toNumber(args.TEMPO); + var change = Cast.toNumber(args.TEMPO); if (typeof this.runtime.audioEngine === 'undefined') return; - this.runtime.audioEngine.changeTempo(value); + var tempo = change + this.runtime.audioEngine.currentTempo; + this._updateTempo(tempo); +}; + +Scratch3SoundBlocks.prototype._updateTempo = function (tempo) { + tempo = MathUtil.clamp(tempo, Scratch3SoundBlocks.TEMPO_RANGE.min, Scratch3SoundBlocks.TEMPO_RANGE.max); + if (typeof this.runtime.audioEngine === 'undefined') return; + this.runtime.audioEngine.setTempo(tempo); }; Scratch3SoundBlocks.prototype.getTempo = function () { From 106db6a0244e788b6821a2e36bf38d8b09d5bae2 Mon Sep 17 00:00:00 2001 From: Eric Rosenbaum Date: Mon, 20 Mar 2017 14:53:04 -0400 Subject: [PATCH 4/4] Clamp range for each audio effect --- src/blocks/scratch3_sound.js | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/blocks/scratch3_sound.js b/src/blocks/scratch3_sound.js index 696ece4a8..c60e41fb0 100644 --- a/src/blocks/scratch3_sound.js +++ b/src/blocks/scratch3_sound.js @@ -51,6 +51,18 @@ Scratch3SoundBlocks.BEAT_RANGE = {min: 0, max: 100}; */ Scratch3SoundBlocks.TEMPO_RANGE = {min: 20, max: 500}; + /** The minimum and maximum values for each sound effect. + * @type {{effect:{min: number, max: number}}} + */ +Scratch3SoundBlocks.EFFECT_RANGE = { + pitch: {min: -600, max: 600}, // -5 to 5 octaves + pan: {min: -100, max: 100}, // 100% left to 100% right + echo: {min: 0, max: 100}, // 0 to max (75%) feedback + reverb: {min: 0, max: 100}, // wet/dry: 0 to 100% wet + fuzz: {min: 0, max: 100}, // wed/dry: 0 to 100% wet + robot: {min: 0, max: 600} // 0 to 5 octaves +}; + /** * @param {Target} target - collect sound state for this target. * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary. @@ -193,25 +205,29 @@ Scratch3SoundBlocks.prototype.setInstrument = function (args, util) { }; Scratch3SoundBlocks.prototype.setEffect = function (args, util) { - var effect = Cast.toString(args.EFFECT).toLowerCase(); - var value = Cast.toNumber(args.VALUE); - - var soundState = this._getSoundState(util.target); - if (!soundState.effects.hasOwnProperty(effect)) return; - - soundState.effects[effect] = value; - if (util.target.audioPlayer === null) return; - util.target.audioPlayer.setEffect(effect, soundState.effects[effect]); + this._updateEffect(args, util, false); }; Scratch3SoundBlocks.prototype.changeEffect = function (args, util) { + this._updateEffect(args, util, true); +}; + +Scratch3SoundBlocks.prototype._updateEffect = function (args, util, change) { var effect = Cast.toString(args.EFFECT).toLowerCase(); var value = Cast.toNumber(args.VALUE); var soundState = this._getSoundState(util.target); if (!soundState.effects.hasOwnProperty(effect)) return; - soundState.effects[effect] += value; + if (change) { + soundState.effects[effect] += value; + } else { + soundState.effects[effect] = value; + } + + var effectRange = Scratch3SoundBlocks.EFFECT_RANGE[effect]; + soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], effectRange.min, effectRange.max); + if (util.target.audioPlayer === null) return; util.target.audioPlayer.setEffect(effect, soundState.effects[effect]); };