(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 (for master volume) var gain = context.createGain(); // Equalizer var NUM_BANDS = 10; var peakings = new Array(NUM_BANDS); // Center frequency var frequency = 31.25; for (var i = 0; i < NUM_BANDS; i++) { // Create the instance of BiquadFilterNode var peaking = context.createBiquadFilter(); // Calculate center frequency if (i !== 0) { frequency *= 2; } // Set parameters peaking.type = (typeof peaking.type === 'string') ? 'peaking' : 5; peaking.frequency.value = frequency; peaking.Q.value = 2; peaking.gain.value = document.getElementById('range-equalizer-' + frequency).valueAsNumber; peakings[i] = peaking; } // Flag for starting or stopping sound var isStop = true; // for drawing spectrum // Create the instance of AnalyserNode var analyser = context.createAnalyser(); analyser.minDecibels = -150; // Default -100 dB analyser.maxDecibels = 0; // Default -30 dB var canvases = { low : null, high : null }; var contexts = { low : null, high : null }; canvases.low = document.querySelectorAll('canvas')[0]; canvases.high = document.querySelectorAll('canvas')[1]; contexts.low = canvases.low.getContext('2d'); contexts.high = canvases.high.getContext('2d'); var intervalids = []; var drawSpectrum = function(canvas, canvasContext, size, frequency) { var width = canvas.width; var height = canvas.height; var paddingTop = 20; var paddingBottom = 20; var paddingLeft = 30; var paddingRight = 30; var innerWidth = width - paddingLeft - paddingRight; var innerHeight = height - paddingTop - paddingBottom; var innerBottom = height - paddingBottom; var range = analyser.maxDecibels - analyser.minDecibels; // 70 dB // Frequency Resolution var fsDivN = context.sampleRate / analyser.fftSize; // This value is the number of samples during "frequency" Hz var nHz = Math.floor(frequency / fsDivN); // Get data for drawing spectrum (dB) var spectrums = new Float32Array(size); analyser.getFloatFrequencyData(spectrums); // Clear previous data canvasContext.clearRect(0, 0, canvas.width, canvas.height); // Draw spectrum (dB) canvasContext.beginPath(); for (var i = 0, len = spectrums.length; i < len; i++) { var x = Math.floor((i / len) * innerWidth) + paddingLeft; var y = Math.floor(-1 * ((spectrums[i] - analyser.maxDecibels) / range) * innerHeight) + paddingTop; if (i === 0) { canvasContext.moveTo(x, y); } else { canvasContext.lineTo(x, y); } if ((i % nHz) === 0) { var text = ''; if (frequency < 1000) { text = (frequency * (i / nHz)) + ' Hz'; // index -> frequency } else { text = (parseInt(frequency / 1000) * (i / nHz)) + ' kHz'; // index -> frequency } // Draw grid (X) canvasContext.fillStyle = 'rgba(255, 0, 0, 1.0)'; canvasContext.fillRect(x, paddingTop, 1, innerHeight); // Draw text (X) canvasContext.fillStyle = 'rgba(255, 255, 255, 1.0)'; canvasContext.font = '12px "Times New Roman"'; canvasContext.fillText(text, (x - (canvasContext.measureText(text).width / 2)), (height - 3)); } } canvasContext.strokeStyle = 'rgba(0, 0, 255, 1.0)'; canvasContext.lineWidth = 4; canvasContext.lineCap = 'round'; canvasContext.lineJoin = 'miter'; canvasContext.stroke(); // Draw grid and text (Y) for (var i = analyser.minDecibels; i <= analyser.maxDecibels; i += 10) { var gy = Math.floor(-1 * ((i - analyser.maxDecibels) / range) * innerHeight) + paddingTop; // Draw grid (Y) canvasContext.fillStyle = 'rgba(255, 0, 0, 1.0)'; canvasContext.fillRect(paddingLeft, gy, innerWidth, 1); // Draw text (Y) canvasContext.fillStyle = 'rgba(255, 255, 255, 1.0)'; canvasContext.font = '12px "Times New Roman"'; canvasContext.fillText((i + ' dB'), 3, gy); } }; /* * 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); // Stop drawing spectrum intervalids.forEach(function(intervalid) { window.clearInterval(intervalid); }); intervalids = []; } 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) -> AnalyserNode (Visualization) -> AudioDestinationNode (Output) gain.connect(analyser); analyser.connect(context.destination); // Clear connection oscillator.disconnect(0); peakings.forEach(function(peaking) { peaking.disconnect(0); }); if (document.getElementById('toggle-effect').checked) { // Equalizer ON // Connect nodes for effect (Equalizer) sound // OscillatorNode (Input) -> BiquadFilterNode (Peaking Filter x 10) -> GainNode (Master Volume) (-> AnalyserNode (Visualization) -> AudioDestinationNode (Output)) oscillator.connect(peakings[0]); peakings.forEach(function(peaking, index) { if (index < (NUM_BANDS - 1)) { peaking.connect(peakings[index + 1]); } else { peaking.connect(gain); } }); } else { // Equalizer OFF // OscillatorNode (Input) -> GainNode (Master volume) (-> AnalyserNode (Visualization) -> AudioDestinationNode (Output)) oscillator.connect(gain); } // Set parameters oscillator.type = type; oscillator.frequency.value = frequency; // Start sound oscillator.start(0); // Start drawing spectrum intervalids.push(window.setInterval(function() { drawSpectrum(canvases.low, contexts.low, 16, 62.5); }, 20)); intervalids.push(window.setInterval(function() { drawSpectrum(canvases.high, contexts.high, 750, 1000); }, 20)); isStop = false; this.classList.remove('key-off'); this.classList.add('key-on'); }, false); // Stop sound element.addEventListener(EventWrapper.END, function() { if (isStop) { return; } // Stop sound oscillator.stop(0); // Stop drawing spectrum intervalids.forEach(function(intervalid) { window.clearInterval(intervalid); }); intervalids = []; 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 = gain.gain.minValue || 0; var max = gain.gain.maxValue || 1; if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) { gain.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); // Toggle Effect document.getElementById('toggle-effect').addEventListener(EventWrapper.CLICK, function() { // Clear connection oscillator.disconnect(0); peakings.forEach(function(peaking) { peaking.disconnect(0); }); if (this.checked) { // Equalizer ON // Connect nodes for effect (Equalizer) sound // OscillatorNode (Input) -> BiquadFilterNode (Peaking Filter x 10) -> GainNode (Master Volume) (-> AnalyserNode (Visualization) -> AudioDestinationNode (Output)) oscillator.connect(peakings[0]); peakings.forEach(function(peaking, index) { if (index < (NUM_BANDS - 1)) { peaking.connect(peakings[index + 1]); } else { peaking.connect(gain); } }); } else { // Equalizer OFF // OscillatorNode (Input) -> GainNode (Master Volume) (-> AnalyserNode (Visualization) -> AudioDestinationNode (Output)) oscillator.connect(gain); } }, false); // Control Equalizer var equalizers = Array.prototype.slice.call(document.querySelectorAll('div.controllers-2-column dl dd [type="range"]'), 0); equalizers.forEach(function(equalizer, index) { equalizer.addEventListener('input', function() { var peaking = peakings[index]; var min = peaking.gain.minValue || -40; var max = peaking.gain.maxValue || 40; if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) { peaking.gain.value = this.valueAsNumber; document.getElementById('output-equalizer-' + this.id.replace('range-equalizer-', '')).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; })();