MMitB - OscillatorNode / GainNode

by Keith McMillen

HTML

<div class='controls'>
    <button class='start'>Start</button>
    <button class='stop' disabled>Stop</button>
</div>
<hr>
<div class='waveforms'>
    <button data-waveform='sine'>Sine</button>
    <button data-waveform='square'>Square</button>
    <button data-waveform='sawtooth'>Sawtooth</button>
    <button data-waveform='triangle'>Triangle</button>
</div>
<hr>
<div class='notes'>
    <button data-note='0'>unison</button>
    <button data-note='300'>minor-third</button>
    <button data-note='600'>tritone</button>
    <button data-note='900'>minor-seventh</button>
    <button data-note='1200'>octave</button>
</div>
<div class='display'>
    <label for='freq'>Frequency <span class='freq'>220Hz</span>
    <input class='freq-slider' name='freq' type='range' min='0' max='20000' step='1' value='220' />
    </label>
    <label for='detune'>Detune <span class='detune'>0 cents</span>
    <input class='detune-slider' name='detune' type='range' min='-1000' max='1000' step='1' value='0' />
    </label>
    <label for='gain'>Gain <span class='gain'>0.25</span>

        <input class='gain-slider' name='gain' type='range' min='0' max='1' step='0.1' value='0.25' />
    </label>
</div>

CSS

body {
    background-color: rgb(250, 250, 250);
}
button {
    display: inline-block;
    margin-bottom: 20px;
}
label {
    display: block;
    font-family: monospace;
    line-height: 20px;
    text-shadow: 1px 1px 3px #ccc;
}
input {
    display: inline-block;
    float: right;
    width: 45%;
}
span {
    display: inline-block;
    width: 90px;
    text-align: center;
    font-weight: bold;
    border: 1px solid #efefef;
    border-radius: 3px;
    background-color: white;
}
hr {
    border-color: rgba(200, 200, 200, 0.1);
}

JavaScript

// create our AudioContext and Oscillator Nodes
var audioContext, osc, gain;
// assign our sliders and buttons to variables 
var startButton = document.querySelector('.start'),
    stopButton = document.querySelector('.stop'),
    waveformButtons = document.querySelectorAll('.waveforms button'),
    freqSlider = document.querySelector('.freq-slider'),
    detuneSlider = document.querySelector('.detune-slider'),
    gainSlider = document.querySelector('.gain-slider'),
    gainDisplay = document.querySelector('.gain'),
    freqDisplay = document.querySelector('.freq'),
    detuneDisplay = document.querySelector('.detune');

// load our default value
init();

// setup start/stop
startButton.onclick = start;
stopButton.onclick = stop;

// setup waveform changes
addEventListenerBySelector('.waveforms button', 'click', function (event) {
    var type = event.target.dataset.waveform;
    changeType(type);
});

// setup note changes using detune
addEventListenerBySelector('.notes button', 'click', function (event) {
    var note = event.target.dataset.note;
    changeDetune(note);
});

// update frequency when slider moves
freqSlider.oninput = function () {
    changeFreq(freqSlider.value);
}

// detune frequency when slider moves
detuneSlider.oninput = function () {
    changeDetune(detuneSlider.value);
}

// update gain when slider moves
gainSlider.oninput = function () {
    changeGain(gainSlider.value);
}

function init() {
    audioContext = new(window.AudioContext || window.webkitAudioContext)();
    gain = audioContext.createGain();
    gain.gain.value = 0.25;
    osc = audioContext.createOscillator();
    osc.type = 'sine';
    osc.frequency.value = 440;
    osc.detune.value = 0;
    osc.connect(gain);
    osc.start(0);
}

// start everything by connecting to destination
function start() {
    UI('start');
    gain.connect(audioContext.destination);
}

// stop everything by connecting to destination
function stop() {
    UI('stop');
   ...