Skip to content

Commit

Permalink
feat(DelayNode): Implemented DelayNode and created a demo with it
Browse files Browse the repository at this point in the history
  • Loading branch information
meszaros-lajos-gyorgy committed Feb 7, 2019
1 parent 6a9ace7 commit f5ba3c9
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 3 deletions.
16 changes: 14 additions & 2 deletions poc/common/virtual-webaudio.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions poc/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ <h1>virtual-webaudio demos</h1>
<li><a href="/envelope-monosynth">envelope monosynth</a></li>
<li><a href="/offline-audio-context">offline audio context</a></li>
<li><a href="/scheduled-sequencing">scheduled sequencing</a></li>
<li><a href="/delayed-monosynth">delayed monosynth</a></li>
</ul>
<script src="/reload/reload.js"></script>
</body>
Expand Down
22 changes: 22 additions & 0 deletions poc/monosynth-with-delay/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Monosynth with delay</title>
</head>
<body>
<nav>
<a href="/">demo index</a> » <b>monosynth with delay</b>
</nav>
<h2>A simple, 1 octave, monophonic synthesizer, that you can play with the keyboard with added 5 stage delay effect</h2>
Press the following keys to play the monosynth:
<pre>
W E T Y U
A S D F G H J K
</pre>
<script src="../common/virtual-webaudio.js"></script>
<script src="../common/helpers.js"></script>
<script src="./script.js"></script>
<script src="/reload/reload.js"></script>
</body>
</html>
136 changes: 136 additions & 0 deletions poc/monosynth-with-delay/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/* global virtualWebaudio, AudioContext */

const { VirtualAudioContext, patch, render, diff } = virtualWebaudio

const sound = (frequency, volume, delayInSeconds) => {
const ctx = new VirtualAudioContext()

const osc = ctx.createOscillator()
const gain = ctx.createGain()

osc.frequency.linearRampToValueAtTime(frequency, ctx.currentTime + 0.01)
osc.type = 'triangle'

gain.gain.linearRampToValueAtTime(volume, ctx.currentTime + delayInSeconds)

const delays = [
{ delay: ctx.createDelay(2), delayGain: ctx.createGain() },
{ delay: ctx.createDelay(2), delayGain: ctx.createGain() },
{ delay: ctx.createDelay(2), delayGain: ctx.createGain() },
{ delay: ctx.createDelay(2), delayGain: ctx.createGain() },
{ delay: ctx.createDelay(2), delayGain: ctx.createGain() }
]
delays.forEach(({ delay, delayGain }, index) => {
delayGain.gain.value = 0.9 - index / 5
delay.delayTime.value = 0.3 * index

gain.connect(delay)
delay.connect(delayGain)
delayGain.connect(ctx.destination)
})

osc.connect(gain)
gain.connect(ctx.destination)

osc.start()

return ctx
}

const frequencies = [
261.6,
277.1,
293.6,
311.1,
329.6,
349.2,
369.9,
391.9,
415.3,
440,
466.1,
493.8,
523.2
]
const keyMap = {
A: 0,
W: 1,
S: 2,
E: 3,
D: 4,
F: 5,
T: 6,
G: 7,
Z: 8,
Y: 8,
H: 9,
U: 10,
J: 11,
K: 12
}

let ctx
let old = null

const change = (virtualCtx, ctx) => {
if (old === null) {
render(virtualCtx, ctx)
} else {
patch(diff(old, virtualCtx), ctx)
}

old = virtualCtx
}

let lastPressedNoteIndex = null
let isNotePlaying = false
const keyPressed = {
A: false,
W: false,
S: false,
E: false,
D: false,
F: false,
T: false,
G: false,
Z: false,
Y: false,
H: false,
U: false,
J: false,
K: false
}

document.body.addEventListener('keydown', e => {
if (!ctx) {
ctx = new AudioContext()
}
const pressedChar = String.fromCharCode(e.keyCode)
if (!keyPressed[pressedChar]) {
keyPressed[pressedChar] = true

if (keyMap.hasOwnProperty(pressedChar)) {
if (lastPressedNoteIndex !== keyMap[pressedChar] || !isNotePlaying) {
lastPressedNoteIndex = keyMap[pressedChar]
isNotePlaying = true

change(sound(frequencies[lastPressedNoteIndex], 1, 1), ctx)
}
}
}
})

document.body.addEventListener('keyup', e => {
const pressedChar = String.fromCharCode(e.keyCode)
if (keyPressed[pressedChar]) {
keyPressed[pressedChar] = false

if (keyMap.hasOwnProperty(pressedChar)) {
if (lastPressedNoteIndex === keyMap[pressedChar] && isNotePlaying) {
lastPressedNoteIndex = keyMap[pressedChar]
isNotePlaying = false
change(sound(frequencies[lastPressedNoteIndex], 0, 1), ctx)
}
}
}
})
8 changes: 7 additions & 1 deletion src/VirtualAudioContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Events from './Events'
import VirtualOscillatorNode from './VirtualOscillatorNode'
import VirtualGainNode from './VirtualGainNode'
import VirtualPeriodicWave from './VirtualPeriodicWave'
import VirtualDelayNode from './VirtualDelayNode'

class VirtualAudioContext {
constructor () {
Expand Down Expand Up @@ -37,7 +38,12 @@ class VirtualAudioContext {
createChannelSplitter()
createConstantSource()
createConvolver()
createDelay()
*/
createDelay (maxDelayTime = 1) {
const id = this._.uniqueIdGenerator.generate()
return new VirtualDelayNode(id, this, maxDelayTime)
}
/*
createDynamicsCompressor()
*/
createGain () {
Expand Down
22 changes: 22 additions & 0 deletions src/VirtualDelayNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import VirtualAudioNode from './VirtualAudioNode'
import VirtualAudioParam from './VirtualAudioParam'

import { EVENTS } from './constants'

class VirtualDelayNode extends VirtualAudioNode {
constructor (id, ctx, ...args) {
super(id, ctx)

// TODO: add validation
// * maxDelayTime is an integer between 0 and 180, default 1
// * delayTime is an integer between 0 and maxDelayTime, default 0

const [ maxDelayTime ] = args
this._.maxDelayTime = maxDelayTime
this.delayTime = new VirtualAudioParam(id, ctx, 'delayTime', 0)

ctx._.events.add(EVENTS.CREATE, 'delay', id, ctx.currentTime, args)
}
}

export default VirtualDelayNode
5 changes: 5 additions & 0 deletions src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ const applyEventToContext = curry(({ targetId, eventName, param, time, args }, c
setNodeById(targetId, node, ctx)
}
break
case 'delay': {
const node = apply(ctx.createDelay.bind(ctx), args)
setNodeById(targetId, node, ctx)
}
break
default: {
console.error('unknown node type', param)
}
Expand Down

0 comments on commit f5ba3c9

Please sign in to comment.