(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; } // for legacy browsers context.createScriptProcessor = context.createScriptProcessor || context.createJavaScriptNode; // for selecting optimized buffer size var getBufferSize = function() { if (/(Win(dows )?NT 6\.2)/.test(navigator.userAgent)) { return 1024; // Windows 8 } else if (/(Win(dows )?NT 6\.1)/.test(navigator.userAgent)) { return 1024; // Windows 7 } else if (/(Win(dows )?NT 6\.0)/.test(navigator.userAgent)) { return 2048; // Windows Vista } else if (/Win(dows )?(NT 5\.1|XP)/.test(navigator.userAgent)) { return 4096; // Windows XP } else if (/Mac|PPC/.test(navigator.userAgent)) { return 1024; // Mac OS X } else if (/Linux/.test(navigator.userAgent)) { return 8192; // Linux } else if (/iPhone|iPad|iPod/.test(navigator.userAgent)) { return 2048; // iOS } else { return 16384; // Otherwise } }; // Create the instance of ScriptProcessorNode var processor = context.createScriptProcessor(getBufferSize(), 2, 2); // for iOS var dummy = context.createBufferSource(); // Create the instance of AnalyserNode var analyser = context.createAnalyser(); var canvas = document.querySelector('canvas'); var canvasContext = canvas.getContext('2d'); var drawWave = function() { 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 middle = (innerHeight / 2) + paddingTop; // Sampling period var period = 1 / context.sampleRate; // This value is the number of samples during 5 msec var n5msec = Math.floor(5 * Math.pow(10, -3) * context.sampleRate); // Get data for drawing sound wave var times = new Uint8Array(analyser.fftSize); analyser.getByteTimeDomainData(times); // Clear previous data canvasContext.clearRect(0, 0, width, height); // Draw sound wave canvasContext.beginPath(); for (var i = 0, len = times.length; i < len; i++) { var x = Math.floor((i / len) * innerWidth) + paddingLeft; var y = Math.floor((1 - (times[i] / 255)) * innerHeight) + paddingTop; if (i === 0) { canvasContext.moveTo(x, y); } else { canvasContext.lineTo(x, y); } // 5 msec ? if ((i % n5msec) === 0) { var sec = i * period; // index -> time var msec = sec * Math.pow(10, 3); // sec -> msec var text = Math.round(msec) + ' msec'; // 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 = '16px "Times New Roman"'; canvasContext.fillText(text, (x - (canvasContext.measureText(text).width / 2)), (height - 3)); } } canvasContext.strokeStyle = 'rgba(0, 0, 255, 1.0)'; canvasContext.lineWidth = 2; canvasContext.lineCap = 'round'; canvasContext.lineJoin = 'miter'; canvasContext.stroke(); // Draw grid (Y) canvasContext.fillStyle = 'rgba(255, 0, 0, 1.0)'; canvasContext.fillRect(paddingLeft, paddingTop, innerWidth, 1); canvasContext.fillRect(paddingLeft, middle, innerWidth, 1); canvasContext.fillRect(paddingLeft, innerBottom, innerWidth, 1); // Draw text (Y) canvasContext.fillStyle = 'rgba(255, 255, 255, 1.0)'; canvasContext.font = '16px "Times New Roman"'; canvasContext.fillText(' 1.00', 3, paddingTop); canvasContext.fillText(' 0.00', 3, middle); canvasContext.fillText('-1.00', 3, innerBottom); }; // Sound parameters var volume = document.getElementById('range-volume').valueAsNumber; var type = document.forms['form-wave-type'].elements['radio-wave-type'][0].value; var frequency = document.getElementById('range-frequency').valueAsNumber; // Flag for starting or stopping sound var isStop = true; /* * Event Listener */ // Start or Stop sound document.querySelector('button').addEventListener(EventWrapper.CLICK, async function() { await context.resume(); if (isStop) { // Start onaudioprocess event // (AudioBufferSourceNode ->) ScriptProcessorNode (Input) -> AnalyserNode (Visualization) -> AudioDestinationNode (Output) dummy.connect(processor); processor.connect(analyser); analyser.connect(context.destination); var fs = context.sampleRate; // Sampling frequency var n = 0; // Phase processor.onaudioprocess = function(event) { // Get the instance of Float32Array for output data (Array size equals buffer size) var outputLs = event.outputBuffer.getChannelData(0); // Left channel var outputRs = event.outputBuffer.getChannelData(1); // Right channel for (var i = 0; i < this.bufferSize; i++) { // Fundamental period var t0 = fs / frequency; var output = 0; switch (type) { case 'sine' : output = Math.sin((2 * Math.PI * frequency * n) / fs); break; case 'square' : output = (n < (t0 / 2)) ? 1 : -1; break; case 'sawtooth' : var s = 2 * n / t0; output = s - 1; break; case 'triangle' : var s = 4 * n / t0; output = (n < (t0 / 2)) ? (-1 + s) : (3 - s); break; default : break; } // Output sound outputLs[i] = volume * output; outputRs[i] = volume * output; // Update phase n++; // Exceed fundamental period ? if (n >= t0) { n = 0; } } // Draw sound wave drawWave(); }; isStop = false; this.innerHTML = '<span class="icon-pause"></span>'; } else { // Stop onaudioprocess event processor.disconnect(); processor.onaudioprocess = null; isStop = true; this.innerHTML = '<span class="icon-start"></span>'; } }, false); // Control Volume (without GainNode) document.getElementById('range-volume').addEventListener('input', function() { var min = 0; var max = 1; if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) { volume = this.valueAsNumber; document.getElementById('output-volume').textContent = this.value; } }, false); // Select Wave 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) { type = this.elements['radio-wave-type'][i].value; break; } } }, false); // Control Frequency document.getElementById('range-frequency').addEventListener('input', function() { var min = 0; if (this.valueAsNumber >= min) { frequency = this.valueAsNumber; document.getElementById('output-frequency').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; })();