Sonr is sound design toolkit for compact and expressive sound description.
import {sin, adsr, play} from 'sonr'
play(sin(432 + 10*sin(8)) * adsr(0, 0, .4, 0)) // fm sine oscillator
// === zzfx.play(...[,0,925,,.4,0,,,,,,,,,5]);
- describing complex sounds with tiny footprint (serializable to string);
- reproducing basic synthesizers
- alternative to heavy soundfonts (like this);
- variable sounds (by analogy with variable fonts) − in reality sounds are variable;
- just fun.
Initially inspired by zzfx, decomposed to more generic, streamline and descriptive form with operators overloading.
// zzfx(...[,,1675,,.06,.24,1,1.82,,,837,.06]); // coin
tri(1675 + jump(0.06, 837)) * adsr(0, 0, .06, .24) | shape(1.82)
// core (sonr.js)
// define sound
$() // silence
$(440) // constant
$(sin(440) * .5 + .5) // expression (see below)
$(time => value) // generate from function, null = end
$([a, b, c]) // sequence
$({0: a, .1:b, .25:c}) // composition
$('path/to/sound.mp3') // from path or url
$('...base64') // from base64 data
$(new Uint8Array([...])) // uint8 samples 0..255
$(new Float32Array([...])) // float samples -1..+1
$(AudioBuffer) // web-audio buffer
// expressions
sin(220) + sin(440) + c(.5) // mix/sum
sin(220) * adsr(.5,.2,.1,.1) * c(.5) // modulate/amplify
-sin(220) - sin(440) - c(.5) // invert phase
sin(220) | shape(.4) | gain(+10) // pipe/map/filter/transform
sin(220) & sin(440) & sin(660) // channel/group
gen(t => sample, rate=1) // generate samples
| map((x, t) => y) // map samples (no arg - bypass)
// controlling
play(src) // or a | play
save(src) // or b | save
plot(type=1, params) // 1=waveform, 2=spectrum, 3=spectrogram
// oscillators (sonr/osc.js)
osc(phase => sample, freq=220, phase=0) // generate oscillation by periodic function
sin(f, phase), cos(f, phase), saw(f, phase),
tri(f, phase), rect(f, phase), tan(f, phase) // predefined oscillators
wave(seed, freq=220, phase=0) // periodic wave oscillator, wave(123) === [1/9,2/9,3/9] overtone weights
// noises (sonr/noise.js)
noise(type) // -2=red (brown), -1=pink, 0=white, 1=blue, 2=violet (purple)
white(), red(), brown(), pink(), blue(), purple(), gray()
color(f, q, amp) // formant: generate noise based on frequency, q-factor, amp
// envelopes, curves
line(v, dur), line([v1, v2, v3], step)
adsr(a, d?, s?, svol?, r?), // adsr envelope
// util
rand(val, amt) // randomize value
tap(val, val => log(val)) // catch input value
fft(src) // convert signal to freq domain, process there
// lab transforms (sonr/latr.js)
inc(rate=1) // linear grow (integrator)
| gain(amp, c=0) // amplify signal
| trim(t) // trim to the duration
| dirac()
| delay(t=0, mix=1) // zzfx delay
| shift(t=0) // shift signal in time
| reverb(profile)
| compress()
| shape(curve, oversample) // waveshaper node / zzfx curve
| filter(type=1, f, Q, amp=1) // 1=cut, 2=shelf, 3=bell, 4=tilt, 5=loudness
// windows (sonr/win.js)
win(type)
// ZZFX adapter (sonr/zzfx.js)
zzfx(...params)
| jump(t, amt)
| repeat(frame) // zzfx repeat
// audios (sonr/aud.js)
mix(a, b, c, ...) // input mixer: allows scalar coefs
mux(channels, selector) // multiplexor, selects input by controlling signal
merge(inputChannels, )
mic(vol/params) // signal from microphone
waa(node) // signal from web audio api node
midi() // from midi keyboard
pan(-1..+1) // direct sound to one of the channels
Audio('src').trim() | play // integration with audio.js
// musicals (sonr/mus.js)
note(n='A4'), note`A4`, note.A4
chord(type, root)
sampler([a, b, c], controller)
// choice(arr) === mux([a,b,c], rand(int, 0, 2))
// fft
// vocoder
// reason
Sones support limited operator overloading expressions:
Expression | Limits | Meaning |
---|---|---|
a + b + c… + n |
n < 1e5. |
Mix signals, add constant. |
±m * a * b * c… + n |
m > 0; for irrational m or members > 3, n = 0. |
Amplify/modulate signals, add constant. |
a | b | c … |
a and result must be wrapped to $ . |
Pipe: filter, transform, output. |
For other expressions, use map
, mix
or wrap with $
:
sin(440) | map(x => x ** 2)
mix(sin(220)*.82, -sin(440)*1.08, sin(880)*1.728)
$(sin(220)*.82) + $(-sin(440)*1.08) + $(1.728)
All the following projects served as inspiration and reference:
- Slang: (+ cool name + neat API + great idea - we don't need new language, we have JS).
- ZZFX: (+ performant implementation + tiny size + elaborate stable effects - params are unreadable - no mixing - no filters).
- WAA: (+ standard - hairy - no node support)
- Wasgen: (+ w1234 + great ideas - heavy (WAA) - funky API)
- Soundfonts (+ standard + oldschool - super-heavy)
🕉