Simple Synthesis - Envelope Generator

An addendum to Emmett Corman's Modular Synthesis posts on www.keithmcmillen.com/blog/

by Keith McMillen

HTML

<script src="https://files.keithmcmillen.com/lib/js/OscilloscopeLabs.js"></script>
<script src="https://files.keithmcmillen.com/blog/simple-synthesis/js/Oscilloscope.js"></script>

<div id="content">
    <canvas id="oscilloscope"></canvas>
    <div id="controls">
        <label for='waveType'>VCO type
            <select name="waveType" class="wavetype">
                <option value="sine">sine</option>
                <option value="square">square</option>
                <option value="sawtooth">sawtooth</option>
                <option value="triangle">triangle</option>
            </select>
        </label>
        <hr>
        <p>Envelope Generator (EG)</p>
        <br>
        <label for='egAttack'>Attack (time) <span class='ega-display'>0.1</span>

            <input class='eg-attack' type='range' min='0' max='1' step='0.05' value='0.1' name='egAttack' />
        </label>
        <br>
        <label for='egDecay'>Decay (time) <span class='egd-display'>0.1</span>

            <input class='eg-decay' type='range' min='0' max='1' step='0.05' value='0.1' name='egDecay' />
        </label>
       <br>
        <label for='egSustain'>Sustain (gain) <span class='egs-display'>1</span>

            <input class='eg-sustain' type='range' min='0' max='1' step='0.05' value='1' name='egSustain' />
        </label>
        <br>
        <label for='egRelease'>Release (time) <span class='egr-display'>0.1</span>

            <input class='eg-release' type='range' min='0' max='1' step='0.05' value='0.1' name='egRelease' />
        </label>
        <br>
            <p>EG Mode</p>
        <label for='egHigh'>High
            <input class='eg-high' type='radio' name='egMode' value='10' />
        </label>
        <label for='egMedium'>Medium
            <input class='eg-medium' type='radio' name='egMode' value='5' />
        </label>
        <label for='egLow'>Low
            <input class='eg-low' type='radio' name='egMode' value='1' checked />
        </label>
        <br>
  ...

CSS

* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

*::before,
*::after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

body {
  background-color: rgb(250, 250, 250);
  font-family: monospace;
}

p {
  font-size: 14px;
}

button {
  display: inline-block;
  margin-bottom: 20px;
}

label {
  display: block;
  line-height: 20px;
}

input {
  display: inline-block;
  float: right;
  width: 45%;
}

span {
  display: inline-block;
  width: 50px;
  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);
}

#content {
  position: relative;
  max-width: 800px;
  margin: 0 auto;
  padding-top: 30px;
}

#oscilloscope {
  margin-bottom: 20px;
}

#controller {
  overflow: hidden;
}

#controller svg {
  position: relative;
  top: -55px;
}

@media (min-width: 640px) {
  #controller svg {
    top: -125px;
  }
}

.key {
  cursor: pointer;
}

.key:hover,
.key.active {
  fill: #00ADEE;
}

JavaScript

/* 
* First, patch the gate output of your keyboard into the EG’s gate input, this will trigger the voltage envelope each time you hit a key.

* Next patch from the EG’s output into the VCA’s CV input. Now when you hit a key you will hear articulated and dynamic notes according to your Envelope Generator settings.
*/

// set up our Audio Context
var audioContext = new(window.AudioContext || window.webkitAudioContext);

// set up an oscillator, gain, and master gain
var vco = audioContext.createOscillator(),
  vca = audioContext.createGain(),
  master = audioContext.createGain(),
  a = d = r = 0.1,
  s = 1,
  egMode = 1,
  velocity = 1;

// keep track of active keys
var activeKeys = {};

// setup controls
var waveType = document.querySelector('.wavetype'),
  masterGain = document.querySelector('.master-gain'),
  masterGainDisplay = document.querySelector('.master-gain-display'),
  Attack = document.querySelector('.eg-attack'),
  Decay = document.querySelector('.eg-decay'),
  Sustain = document.querySelector('.eg-sustain'),
  Release = document.querySelector('.eg-release'),
  egHigh = document.querySelector('.egs-high'),
  egMedium = document.querySelector('.egs-medium'),
  egLow = document.querySelector('.egs-low'),
  attackDisplay = document.querySelector('.ega-display'),
  decayDisplay = document.querySelector('.egd-display'),
  sustainDisplay = document.querySelector('.egs-display'),
  releaseDisplay = document.querySelector('.egr-display');

// connect all modules to master 
vco.connect(vca);
vca.connect(master);
master.connect(audioContext.destination);

//set params
vca.gain.value = 0;
master.gain.value = 1;

// start the vco
vco.start(0);


// Controls
waveType.onchange = function() {
  changeWaveType(waveType.value);
}

Attack.oninput = function() {
  changeAttack(Attack.value);
}

Decay.oninput = function() {
  changeDecay(Decay.value);
}

Sustain.oninput = function() {
  changeSustain(Sustain.value);
}

Release.oninput = function() {
 ...