オートワウ

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();

        // 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;
})();