UNPKG

codehs-graphics

Version:

Helpers used to run graphics problems in the CodeHS editor.

241 lines (225 loc) 7.47 kB
'use strict'; /* IMPORTANT NOTE: * In the case that ToneJS is not supported or not on the window, we make all * corresponding Sound functions no-ops so that student code is still able to run. */ /* * Construct a new Sound. * Optionally set the frequency and the oscillator type. * * @param frequency - Either a number (Hertz) or note ("C#4" for middle C Sharp) * @param oscillatorType {string} - several options * basic types: "sine", "triangle", "square", "sawtooth" * any basic type can be prefixed with "fat", "am" or "fm", ie "fatsawtooth" * any basic type can be suffixed with a number ie "4" for the number of partials * ie "square4" * special types: "pwm", "pulse" * drum instrument: "drum" * cymbal instrument: "metal" * https://tonejs.github.io/docs/13.8.25/OmniOscillator */ var Sound = function Sound(frequency, oscillatorType) { if (window.Tone && window.Tone.supported) { this.frequency = frequency || 440; this.oscillatorType = oscillatorType || 'fatsawtooth'; this.volume = 1; if (this.oscillatorType == 'drum') { this.synth = new window.Tone.MembraneSynth().toMaster(); } else if (this.oscillatorType == 'metal') { this.synth = new window.Tone.MetalSynth().toMaster(); } else { this.synth = new window.Tone.Synth({ oscillator: {type: this.oscillatorType}, }).toMaster(); } this.setFrequency(this.frequency); } }; /* * Set the Sound's frequency * * @param frequency - Either a number (Hertz) or note ("C#4" for middle C Sharp) */ Sound.prototype.setFrequency = function(frequency) { if (window.Tone && window.Tone.supported) { this.frequency = frequency; if (this.getOscillatorType() == 'metal') { this.synth.frequency.value = frequency; } else { this.synth.oscillator.frequency.value = frequency; } } }; /* * Set the Sound's volume * * @param {float} - the volume in decibels */ Sound.prototype.setVolume = function(volume) { if (window.Tone && window.Tone.supported) { this.volume = volume; this.synth.volume.value = volume; } }; /* * Get the Sound's frequency * * @returns The Sound's frequency */ Sound.prototype.getFrequency = function() { if (window.Tone && window.Tone.supported) { return this.frequency; } }; /* * Get the Sound's volume * * @returns the volume */ Sound.prototype.getVolume = function() { if (window.Tone && window.Tone.supported) { return this.volume; } }; /* * Set the Sound's oscillator type * * @param oscillatorType {string} - several options * basic types: "sine", "triangle", "square", "sawtooth" * any basic type can be prefixed with "fat", "am" or "fm", ie "fatsawtooth" * any basic type can be suffixed with a number ie "4" for the number of partials * ie "square4" * special types: "pwm", "pulse" * drum instrument: "drum" * cymbal instrument: "metal" * https://tonejs.github.io/docs/13.8.25/OmniOscillator */ Sound.prototype.setOscillatorType = function(oscillatorType) { if (window.Tone && window.Tone.supported) { var originalOscillatorType = this.getOscillatorType(); if (oscillatorType == originalOscillatorType) { return; } this.oscillatorType = oscillatorType; if (oscillatorType == 'drum') { this.disconnect(); this.synth = new window.Tone.MembraneSynth().toMaster(); this.setFrequency(this.getFrequency()); } else if (oscillatorType == 'metal') { this.disconnect(); this.synth = new window.Tone.MetalSynth().toMaster(); this.setFrequency(this.getFrequency()); } else if (originalOscillatorType == 'drum' || originalOscillatorType == 'metal') { this.disconnect(); this.synth = new window.Tone.Synth({ oscillator: {type: oscillatorType}, }).toMaster(); this.setFrequency(this.frequency); } else { this.synth.oscillator.type = oscillatorType; } } }; /* * Get the Sound's oscillator type * * @returns a String representing the oscillator type */ Sound.prototype.getOscillatorType = function() { if (window.Tone && window.Tone.supported) { return this.oscillatorType; } }; /* * Play the sound indefinitely */ Sound.prototype.play = function() { if (window.Tone && window.Tone.supported) { if (this.getOscillatorType() == 'metal') { this.synth.triggerAttack(); } else { this.synth.triggerAttack(this.getFrequency()); } } }; /* * Play the sound for a given duration. * * @param {string} - duration in one of several formats, mainly: * number: the number of seconds to play the sound for. * "2" for 2 seconds * "1.5" for 1.5 seconds * OR * notation: Describes time in BPM and time signature relative values. * "4n" for quarter note * "8t" for eigth note triplet, * "2m" for 2 measures * "8n." for dotted eighth note */ Sound.prototype.playFor = function(duration) { if (window.Tone && window.Tone.supported) { if (this.getOscillatorType() == 'metal') { this.synth.triggerAttackRelease(duration); } else { this.synth.triggerAttackRelease(this.getFrequency(), duration); } } }; /* * Stop playing the sound immediately. */ Sound.prototype.stop = function() { if (window.Tone && window.Tone.supported) { this.synth.triggerRelease(); } }; /* * Disconnect the sound from the AudioNode. * * This generally should not be used by students. We use it to force stop * sounds that are playing when the "STOP" button is pressed in the editor. */ Sound.prototype.disconnect = function() { if (window.Tone && window.Tone.supported) { this.synth.disconnect(); } }; /* * Add an effect to this sound * * @param effectName {String} - the name of the prepackaged effect, ie "reverb" * @param effectValue {float} - value from 0 to 1 defining how heavily the * effect applies */ Sound.prototype.setEffect = function(effectName, effectValue) { if (window.Tone && window.Tone.supported) { switch (effectName) { case 'distortion': var distortion = new window.Tone.Distortion(effectValue).toMaster(); this.synth.connect(distortion); return; case 'chebyshev': var chebyshev = new window.Tone.Chebyshev(effectValue * 100).toMaster(); this.synth.connect(chebyshev); return; case 'reverb': var reverb = new window.Tone.Freeverb().toMaster(); reverb.wet.value = effectValue; this.synth.connect(reverb); return; case 'tremolo': var tremolo = new window.Tone.Tremolo().toMaster().start(); tremolo.wet.value = effectValue; this.synth.connect(tremolo); return; case 'vibrato': var vibrato = new window.Tone.Vibrato().toMaster(); vibrato.wet.value = effectValue; this.synth.connect(vibrato); return; default: return; } } }; module.exports = Sound;