周波数領域の波形描画 | サウンドの視覚化

START / STOP
WAVE TYPE
(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();

        // Parameters for the instance of OscillatorNode
        var type      = oscillator.type;
        var frequency = oscillator.frequency.value;
        var detune    = oscillator.detune.value;

        // for legacy browsers
        context.createGain = context.createGain || context.createGainNode;

        // Create the instance of GainNode
        var gain = context.createGain();

        // Flag for starting or stopping sound
        var isStop = true;

        // for drawing sound wave (spectrum)

        // Create the instance of AnalyserNode
        var analyser = context.createAnalyser();

        var canvas        = document.querySelector('canvas');
        var canvasContext = canvas.getContext('2d');

        var timerid  = null;
        var interval = document.getElementById('range-draw-interval').valueAsNumber;

        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;

            // Frequency resolution
            var fsDivN = context.sampleRate / analyser.fftSize;

            // This value is the number of samples during 500 Hz
            var n500Hz = Math.floor(500 / fsDivN);

            // Get data for drawing spectrum
            var spectrums = new Uint8Array(analyser.frequencyBinCount / 4);
            analyser.getByteFrequencyData(spectrums);

            // Clear previous data
            canvasContext.clearRect(0, 0, width, height);

            // Draw spectrum
            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] / 255)) * innerHeight) + paddingTop;

                if (i === 0) {
                    canvasContext.moveTo(x, y);
                } else {
                    canvasContext.lineTo(x, y);
                }

                // 500 Hz ?
                if ((i % n500Hz) === 0) {
                    var f    = Math.floor(500 * (i / n500Hz));  // index -> frequency
                    var text = (f < 1000) ? (f + ' Hz') : ((f / 1000) + ' kHz');

                    // 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, middle,      innerWidth, 1);
            canvasContext.fillRect(paddingLeft, paddingTop,  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.50', 3, middle);
            canvasContext.fillText('0.00', 3, innerBottom);

            timerid = window.setTimeout(drawWave, interval);
        };

        /*
         * Event Listener
         */

        // Start or Stop sound
        document.querySelector('button').addEventListener(EventWrapper.CLICK, function() {
            if (isStop) {
                // Create the instance of OscillatorNode
                oscillator = context.createOscillator();

                // for legacy browsers
                oscillator.start = oscillator.start || oscillator.noteOn;
                oscillator.stop  = oscillator.stop  || oscillator.noteOff;

                // OscillatorNode (Input) -> GainNode (Volume) -> AnalyserNode (Visualization) -> AudioDestinationNode (Output)
                oscillator.connect(gain)
                gain.connect(analyser);
                analyser.connect(context.destination);

                // Set parameters
                oscillator.type            = type;
                oscillator.frequency.value = frequency;
                oscillator.detune.value    = detune;

                // Start sound
                oscillator.start(0);

                // Start drawing sound wave
                drawWave();

                isStop = false;
                this.innerHTML = '<span class="icon-pause"></span>';
            } else {
                // Stop sound
                oscillator.stop(0);

                // Stop drawing sound wave
                if (timerid !== null) {
                    window.clearTimeout(timerid);
                    timerid = null;
                }

                isStop = true;
                this.innerHTML = '<span class="icon-start"></span>';
            }
        }, false);

        // Control Draw Interval
        document.getElementById('range-draw-interval').addEventListener('input', function() {
            interval = this.valueAsNumber;
            document.getElementById('output-draw-interval').textContent = this.value;
        }, false);

        // Control 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);

        // Control frequency
        document.getElementById('range-frequency').addEventListener('input', function() {
            var min = oscillator.frequency.minValue || 0;
            var max = oscillator.frequency.maxValue || 100000;

            if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
                oscillator.frequency.value = frequency = this.valueAsNumber;
                document.getElementById('output-frequency').textContent = this.value;
            }
        }, false);

        // Control detune
        document.getElementById('range-detune').addEventListener('input', function() {
            var min = oscillator.detune.minValue || -4800;
            var max = oscillator.detune.maxValue ||  4800;

            if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
                oscillator.detune.value = detune = this.valueAsNumber;
                document.getElementById('output-detune').textContent = this.value;
            }
        }, false);

        // Select fftSize
        document.getElementById('select-fft-size').addEventListener('change', function() {
            switch (parseInt(this.value)) {
                case   32 :
                case   64 :
                case  128 :
                case  256 :
                case  512 :
                case 1024 :
                case 2048 :
                    analyser.fftSize = this.value;
                    break;
                default :
                    window.alert('The selected FFT size is invalid.');
                    break;
            }
        }, false);

        // Control smoothingTimeConstant
        document.getElementById('range-smoothing-time-constant').addEventListener('input', function() {
            var min = 0;
            var max = 1;

            if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
                analyser.smoothingTimeConstant = this.valueAsNumber;
                document.getElementById('output-smoothing-time-constant').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;
})();