Web Audio API Experimentation

Learning about sound and stuff

by wio_dude

HTML

<canvas id="player"></canvas>
<br/>
<label for="volume">Volume</label>
<input type="range" id="volume" name="volume" min="0" max="1.5" step="0.01" value="0" />
<span id="volume-out"></span>
<br />
<label for="frequency">Frequency</label>
<input type="range" id="frequency" name="frequency" min="1" max="4" value="2.34" step="0.01" />
<span id="frequency-out"></span>
<br/>
<label for="type">Wave Type</label>
<select id="type" name="type">
  <option value="sine">Sine</option>
  <option value="square">Square</option>
  <option value="sawtooth">Sawtooth</option>
  <option value="triangle">Triangle</option>
</select>
<br/>
<label for="attack">Attack</label>
<input type="range" id="attack" name="attack" min="0" max="0.5" value="0.05" step="0.001" />
<span id="attack-out"></span>
<br/>
<label for="decay">Decay</label>
<input type="range" id="decay" name="decay" min="0" max="0.5" value="0.1" step="0.001" />
<span id="decay-out"></span>
<br/>
<label for="sustain">Sustain</label>
<input type="range" id="sustain" name="sustain" min="0" max="0.5" value="0.3" step="0.001" />
<span id="sustain-out"></span>
<br/>
<label for="release">Release</label>
<input type="range" id="release" name="release" min="0" max="1" value="0.5" step="0.001" />
<span id="release-out"></span>
<br/>

CSS

canvas {
  background-color: black
}

JavaScript

const rangeIds = [
  'attack',
  'decay',
  'sustain',
  'release'
]

const ranges = {}

for (const rangeId of rangeIds) {
  const elem = document.getElementById(rangeId)
  const elemOut = document.getElementById(rangeId + '-out')
  elemOut.innerHTML = elem.value
  ranges[rangeId] = elem
  elem.addEventListener('input', (evt) => {
    elemOut.innerHTML = elem.value
  })
}


const player = document.getElementById('player')
const volume = document.getElementById('volume')
const volumeOut = document.getElementById('volume-out')
const frequency = document.getElementById('frequency')
const frequencyOut = document.getElementById('frequency-out')
const type = document.getElementById('type')
const context = player.getContext('2d')
const audioCtx = new(window.AudioContext || window.webkitAudioContext)()

function sliderToFrequency(value) {
	return 2 * Math.pow(10, value)
}

function nearestPianoKey(frequency) {
	const keyNum = Math.round(12 * Math.log2(frequency / 440) + 49)
  return 440 * Math.pow(2, ((keyNum - 49) / 12))
}

const gain = audioCtx.createGain()
gain.gain.value = volume.value
gain.connect(audioCtx.destination)
volumeOut.innerHTML = volume.value
frequencyOut.innerHTML = nearestPianoKey(sliderToFrequency(frequency.value))


volume.addEventListener('input', (evt) => {
  gain.gain.value = volume.value
  volumeOut.innerHTML = volume.value
})

frequency.addEventListener('input', (evt) => {
  frequencyOut.innerHTML = nearestPianoKey(sliderToFrequency(frequency.value))
})


let OGain

function playSound() {
  // create Oscillator node
  const oscillator = audioCtx.createOscillator()
  const ngain = audioCtx.createGain()
  OGain = ngain
  ngain.connect(gain)
  const t = audioCtx.currentTime
  const f = nearestPianoKey(sliderToFrequency(frequency.value))

  let ct = t
  let at = ct + parseFloat(ranges.attack.value)
  let dt = at + parseFloat(ranges.decay.value)
  let st = dt + parseFloat(ranges.sustain.value)
  let rt = st + parseFloat(ranges.release.value)
 ...