Skip to content

Instantly share code, notes, and snippets.

@charlieroberts
Last active April 21, 2022 02:58
Show Gist options
  • Save charlieroberts/f17737cd51b0dffc5ffa2de59a39830e to your computer and use it in GitHub Desktop.
Save charlieroberts/f17737cd51b0dffc5ffa2de59a39830e to your computer and use it in GitHub Desktop.
Benchmark comparisons of common synthesis tasks between Genish.js and the Web Audio API, for the 2017 Web Audio Conference
<!doctype html>
<html>
<head>
<!-- npm install mathjs [email protected] -->
<script src='./node_modules/genish.js/dist/gen.lib.js'></script>
<script src='./node_modules/mathjs/dist/math.js'></script>
</head>
<body></body>
<script>
if( typeof OfflineAudioContext === 'undefined' ) window.OfflineAudioContext = webkitOfflineAudioContext
const results = {}
const debug = false
const tests = {
sine_genish: {
run() {
let count = this.count
let ctx = new OfflineAudioContext( 2, 44100 * this.seconds, 44100 )
let g = genish
g.utilities.ctx = ctx
let sines = []
for( let i = 0; i < count; i++ ) {
let s = g.cycle( 440 )
sines.push( s )
}
g.utilities.playGraph( g.add( ...sines ) )
g.utilities.createScriptProcessor()
let start = performance.now()
ctx.startRendering()
ctx.oncomplete = e => {
let end = performance.now()
if( debug ) console.log( 'genish sine rendering time:', end - start )
tests.sine_genish.times.push( end - start )
run()
}
},
times:[],
count:100,
seconds:60
},
sine_waapi: {
run() {
let count = this.count
let ctx = new OfflineAudioContext( 2, 44100*60, 44100 )
for( let i = 0; i < count; i++ ) {
let s = ctx.createOscillator()
s.connect( ctx.destination )
s.start()
}
let start = performance.now()
ctx.startRendering()
ctx.oncomplete = e => {
let end = performance.now()
if( debug ) console.log( 'waapi sine rendering time:', end - start )
tests.sine_waapi.times.push( end-start)
run()
}
},
times:[],
count:100,
seconds:60,
},
vibrato_genish: {
run() {
let count = this.count
let ctx = new OfflineAudioContext( 2, 44100*this.seconds, 44100 )
let g = genish
g.utilities.ctx = ctx
g.utilities.createScriptProcessor()
let sines = []
for( let i = 0; i < count; i++ ) {
let m = g.cycle( 4 )
let c = g.cycle( g.add( 440 + i, g.mul( m, 10 ) ) )
sines.push( c )
}
g.utilities.playGraph( g.add( ...sines ) )
const start = performance.now()
ctx.startRendering()
ctx.oncomplete = e => {
const end = performance.now()
if( debug ) console.log( 'genish vibrato rendering time:', end - start )
tests.vibrato_genish.times.push( end-start )
run()
}
},
times:[],
count:50,
seconds:60
},
vibrato_waapi: {
run() {
let count = this.count
let ctx = new OfflineAudioContext( 2, 44100*this.seconds, 44100 )
for( let i = 0; i < count; i++ ) {
let osc = ctx.createOscillator()
osc.frequency.value = 440 + i
let mod = ctx.createOscillator()
mod.frequency.value = 4
let gainNode = ctx.createGain()
gainNode.gain.value = 10
mod.connect( gainNode )
gainNode.connect( osc.frequency )
gainNode.connect( ctx.destination )
osc.start()
mod.start()
}
const start = performance.now()
ctx.startRendering()
ctx.oncomplete = e => {
const end = performance.now()
if( debug ) console.log( 'waapi vibrato rendering time:', end - start )
tests.vibrato_waapi.times.push( end-start )
run()
}
},
times:[],
count:50,
seconds:60
},
envelopedSaw_genish: {
run() {
let count = this.count
let ctx = new OfflineAudioContext( 2, 44100 * this.seconds, 44100 )
let g = genish
g.utilities.ctx = ctx
let syns = []
for( let i = 0; i < count; i++ ) {
let env = g.ad( 44100 * this.seconds / 2, 44100 * this.seconds / 2 )
let osc = g.phasor( 440 )
let graph = g.mul( env, osc )
graph.env = env
syns.push( graph )
}
g.utilities.playGraph( g.add( ...syns ) )
for( let graph of syns ) graph.env.trigger()
g.utilities.createScriptProcessor()
const start = performance.now()
ctx.startRendering()
ctx.oncomplete = e => {
const end = performance.now()
if( debug ) console.log( 'genish vibrato rendering time:', end - start )
tests.envelopedSaw_genish.times.push( end-start )
run()
}
},
times:[],
count:50,
seconds:60
},
envelopedSaw_waapi: {
run() {
let count = this.count
let ctx = new OfflineAudioContext( 2, 44100*this.seconds, 44100 )
let attack = 44100 * this.seconds / 2
let decay = 44100 * this.seconds / 2
for( let i = 0; i < count; i++ ) {
osc = ctx.createOscillator()
osc.type = 'sawtooth'
osc.frequency.value = 440
gainNode = ctx.createGain()
gainNode.gain.setValueAtTime( 0, ctx.currentTime )
gainNode.gain.linearRampToValueAtTime( 1, ctx.currentTime + attack )
gainNode.gain.linearRampToValueAtTime( 0, ctx.currentTime + attack + decay )
osc.connect( gainNode )
gainNode.connect( ctx.destination )
osc.start()
}
const start = performance.now()
ctx.startRendering()
ctx.oncomplete = e => {
const end = performance.now()
if( debug ) console.log( 'waapi vibrato rendering time:', end - start )
tests.envelopedSaw_waapi.times.push( end-start )
run()
}
},
times:[],
count:50,
seconds:60
},
}
const run = () => {
let test = benchmarks.next().value
if( test !== undefined )
test.run()
else
console.log( 'tests completed' )
}
const factory = function*() {
let keys = Object.keys( tests )
let index = 0
let count = 0
let max = 11
while( index < keys.length && count < max ) {
let test = tests[ keys[ index++ ] ]
if( index === keys.length ) {
if( count++ < max ) {
console.log( 'round', count, 'of', max-1, 'complete.' )
index = 0
}else{
break;
}
}
yield test
}
console.log(`
**************************
results (${max-1} samples)
**************************
`)
for( let testName in tests ) {
let test = tests[ testName ]
// ignore first results via calls to slice(), which are bad for genish
// due to initial compile times
let std = math.std( tests[ testName ].times.slice(0,-1) )
let mean = math.mean( tests[ testName ].times.slice(0,-1) )
let median = math.median( tests[ testName ].times.slice(0,-1) )
let count_seconds = `count:${test.count}, seconds:${test.seconds}`
let output = `${testName} :
${count_seconds}
mean : ${mean}
median : ${median}
std : ${std}
`
console.log( output )
}
}
window.benchmarks = factory()
console.log( 'starting benchmarks...' )
benchmarks.next().value.run()
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment