(function() { var onDOMContentLoaded = function() { window.AudioContext = window.AudioContext || window.webkitAudioContext; try { // Create the instance of AudioContext var context = new AudioContext(); } catch (error) { window.alert(error.message + ' : Please use Chrome or Safari.'); return; } // Create the instance of OscillatorNode var oscillator = context.createOscillator(); // Parameter for the instance of OscillatorNode var type = oscillator.type; // for legacy browsers context.createGain = context.createGain || context.createGainNode; // Create the instance of GainNode var eg = context.createGain(); // for Envelope Generator var masterVolume = context.createGain(); // for Master Volume // for legacy browsers eg.gain.setTargetAtTime = eg.gain.setTargetAtTime || eg.gain.setTargetValueAtTime; // Flag for starting or stopping sound var isStop = true; // Parameters for Envelope Generator var egParams = { attack : 0, decay : 0, sustain : 0, release : 0 }; // Initialization egParams.attack = document.getElementById('range-attack').valueAsNumber; egParams.decay = document.getElementById('range-decay').valueAsNumber; egParams.sustain = document.getElementById('range-sustain').valueAsNumber; egParams.release = document.getElementById('range-release').valueAsNumber; // Auto Wah // Create the instance of BiquadFilterNode var lowpass = context.createBiquadFilter(); lowpass.type = (typeof lowpass.type === 'string') ? 'lowpass' : 0; lowpass.Q.value = 1; lowpass.frequency.value = document.getElementById('range-wah-cutoff').valueAsNumber; // lowpass.gain.value = 0; // Not used var range = 0; var resonance = lowpass.Q; // Initialize parameters for Auto Wah range = parseFloat(document.getElementById('range-wah-range').valueAsNumber / 100); resonance.value = document.getElementById('range-wah-resonance').valueAsNumber; // Flag for starting or stopping sound var isStop = true; // for Envelope Generator var intervalid = null; /* * Event Listener */ // Start or Stop sound var PIANO_88S = [ 'A-4', 'A-4h', 'B-4', 'C-3', 'C-3h', 'D-3', 'D-3h', 'E-3', 'F-3', 'F-3h', 'G-3', 'G-3h', 'A-3', 'A-3h', 'B-3', 'C-2', 'C-2h', 'D-2', 'D-2h', 'E-2', 'F-2', 'F-2h', 'G-2', 'G-2h', 'A-2', 'A-2h', 'B-2', 'C-1', 'C-1h', 'D-1', 'D-1h', 'E-1', 'F-1', 'F-1h', 'G-1', 'G-1h', 'A-1', 'A-1h', 'B-1', 'C', 'Ch', 'D', 'Dh', 'E', 'F', 'Fh', 'G', 'Gh', 'A', 'Ah', 'B', 'C1', 'C1h', 'D1', 'D1h', 'E1', 'F1', 'F1h', 'G1', 'G1h', 'A1', 'A1h', 'B1', 'C2', 'C2h', 'D2', 'D2h', 'E2', 'F2', 'F2h', 'G2', 'G2h', 'A2', 'A2h', 'B2', 'C3', 'C3h', 'D3', 'D3h', 'E3', 'F3', 'F3h', 'G3', 'G3h', 'A3', 'A3h', 'B3', 'C4' ]; var pianoKeys = Array.prototype.slice.call(document.querySelectorAll('#piano ul li'), 0); var convertIndex = function(index) { // // The 12 equal temparement // // Min -> A 27.5Hz, Max -> C 4186 Hz // // Example : // A * 1.059463 -> A# (half up) // var FREQUENCY_RATIO = Math.pow(2, (1 / 12)); // about 1.059463; var MIN_A = 27.5; return (MIN_A * Math.pow(FREQUENCY_RATIO, index)); }; pianoKeys.forEach(function(element, index, array) { // Start sound element.addEventListener(EventWrapper.START, function(event) { if (!isStop) { oscillator.stop(0); // Clear interval if (intervalid !== null) { window.clearInterval(intervalid); intervalid = null; } } var pianoIndex = PIANO_88S.indexOf(this.id); var frequency = convertIndex(pianoIndex); // Create the instance of OscillatorNode oscillator = context.createOscillator(); // for legacy browsers oscillator.start = oscillator.start || oscillator.noteOn; oscillator.stop = oscillator.stop || oscillator.noteOff; // GainNode (Master Volume) -> AudioDestinationNode (Output); masterVolume.connect(context.destination); // Clear connection oscillator.disconnect(0); eg.disconnect(0); lowpass.disconnect(0); /* * Envelope Generator : Attack -> Decay -> Sustain */ var t0 = context.currentTime; var t1 = t0 + egParams.attack; var t2 = egParams.decay; var t2Value = egParams.sustain; if (document.getElementById('toggle-effect').checked) { // Auto Wah ON // Connect nodes for effect (Auto Wah) sound // OscillatorNode (Input) -> GainNode (Envelope Generator) -> BiquadFilterNode (Low-Pass Filter) -> GainNode (Master Volume) (-> AudioDestinationNode (Output)) oscillator.connect(eg); eg.connect(lowpass); lowpass.connect(masterVolume); var maxCutoff = document.getElementById('range-wah-cutoff').valueAsNumber; var minCutoff = maxCutoff * parseFloat(range / 100); lowpass.frequency.cancelScheduledValues(t0); // Start frequency from "minCutoff" lowpass.frequency.setValueAtTime(minCutoff, t0); // Attack : frequency increases linearly until assigned time (t1) lowpass.frequency.linearRampToValueAtTime(maxCutoff, t1); // Decay -> Sustain : freqnency gradually decreases to value of sustain during decay time (t2) from assigned time (t1) lowpass.frequency.setTargetAtTime((maxCutoff * egParams.sustain), t1, t2); } else { // Auto Wah OFF // OscillatorNode (Input) -> GainNode (Envelope Generator) -> GainNode (Master Volume) (-> AudioDestinationNode (Output)) oscillator.connect(eg); eg.connect(masterVolume); } eg.gain.cancelScheduledValues(t0); // Start from gain = 0 eg.gain.setValueAtTime(0, t0); // Attack : gain increases linearly until assigned time (t1) eg.gain.linearRampToValueAtTime(1, t1); // Decay -> Sustain : gain gradually decreases to value of sustain during decay time (t2) from assigned time (t1) eg.gain.setTargetAtTime(t2Value, t1, t2); // Set parameters oscillator.type = type; oscillator.frequency.value = frequency; // Start sound oscillator.start(0); intervalid = window.setInterval(function() { var VALUE_OF_STOP = 1e-3; if (eg.gain.value < VALUE_OF_STOP) { // Stop sound oscillator.stop(0); window.clearInterval(intervalid); intervalid = null; isStop = true; } }, 0); isStop = false; this.classList.remove('key-off'); this.classList.add('key-on'); }, false); // Stop sound element.addEventListener(EventWrapper.END, function() { if (isStop) { return; } /* * Envelope Generator : Sustain -> Release */ var t3 = context.currentTime; var t4 = egParams.release; if (document.getElementById('toggle-effect').checked) { var maxCutoff = document.getElementById('range-wah-cutoff').valueAsNumber; var minCutoff = maxCutoff * parseFloat(range / 100); // in the case of mouseup on the way of Attack or Decay lowpass.frequency.cancelScheduledValues(t3); lowpass.frequency.setValueAtTime(lowpass.frequency.value, t3); // Release : frequency gradually decreases to "minCutoff" during release time (t4) from assigned time (t3) lowpass.frequency.setTargetAtTime(minCutoff, t3, t4); } // in the case of mouseup on the way of Attack or Decay eg.gain.cancelScheduledValues(t3); eg.gain.setValueAtTime(eg.gain.value, t3); // Release : gain gradually decreases to 0 during release time (t4) from assigned time (t3) eg.gain.setTargetAtTime(0, t3, t4); // Not stop sound for Envelope Generator // oscillator.stop(0); // isStop = true; this.classList.remove('key-on'); this.classList.add('key-off'); }, false); }); // Control Master Volume document.getElementById('range-volume').addEventListener('input', function() { var min = masterVolume.gain.minValue || 0; var max = masterVolume.gain.maxValue || 1; if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) { masterVolume.gain.value = this.valueAsNumber; document.getElementById('output-volume').textContent = this.value; } }, false); // Select type document.getElementById('form-wave-type').addEventListener('change', function() { for (var i = 0, len = this.elements['radio-wave-type'].length; i < len; i++) { if (this.elements['radio-wave-type'][i].checked) { oscillator.type = type = (typeof oscillator.type === 'string') ? this.elements['radio-wave-type'][i].value : i; break; } } }, false); // Control Attack document.getElementById('range-attack').addEventListener('input', function() { egParams.attack = this.valueAsNumber; document.getElementById('output-attack').textContent = this.value; }, false); // Control Decay document.getElementById('range-decay').addEventListener('input', function() { egParams.decay = this.valueAsNumber; document.getElementById('output-decay').textContent = this.value; }, false); // Control Sustain document.getElementById('range-sustain').addEventListener('input', function() { egParams.sustain = this.valueAsNumber; document.getElementById('output-sustain').textContent = this.value; }, false); // Control Release document.getElementById('range-release').addEventListener('input', function() { egParams.release = this.valueAsNumber; document.getElementById('output-release').textContent = this.value; }, false); // Toggle Effect document.getElementById('toggle-effect').addEventListener(EventWrapper.CLICK, function() { // Clear connection oscillator.disconnect(0); eg.disconnect(0) lowpass.disconnect(0); if (this.checked) { // Auto Wah ON // Connect nodes for effect (Auto Wah) sound // OscillatorNode (Input) -> GainNode (Envelope Generator) -> BiquadFilterNode (Low-Pass Filter) -> GainNode (Master Volume) (-> AudioDestinationNode (Output)) oscillator.connect(eg); eg.connect(lowpass); lowpass.connect(masterVolume); } else { // Auto Wah OFF // OscillatorNode (Input) -> GainNode (Envelope Generator) -> GainNode (Master Volume) (-> AudioDestinationNode (Output)) oscillator.connect(eg); eg.connect(masterVolume); } }, false); // Control Auto Wah frequency (cutoff) document.getElementById('range-wah-cutoff').addEventListener('input', function() { lowpass.frequency.value = this.valueAsNumber; document.getElementById('output-wah-cutoff').textContent = this.value; }, false); // Control Auto Wah Range document.getElementById('range-wah-range').addEventListener('input', function() { range = this.valueAsNumber; document.getElementById('output-wah-range').textContent = this.value; }, false); // Control Auto Wah Resonance document.getElementById('range-wah-resonance').addEventListener('input', function() { resonance.value = this.valueAsNumber; document.getElementById('output-wah-resonance').textContent = this.value; }, false); }; if ((document.readyState === 'interactive') || (document.readyState === 'complete')) { onDOMContentLoaded(); } else { document.addEventListener('DOMContentLoaded', onDOMContentLoaded, true); } })();
function EventWrapper(){ } (function(){ var click = ''; var start = ''; var move = ''; var end = ''; // Touch Panel ? if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) { click = 'click'; start = 'touchstart'; move = 'touchmove'; end = 'touchend'; } else { click = 'click'; start = 'mousedown'; move = 'mousemove'; end = 'mouseup'; } EventWrapper.CLICK = click; EventWrapper.START = start; EventWrapper.MOVE = move; EventWrapper.END = end; })();