0.0.1 • Published 3 years ago
unisons v0.0.1
unisons
Hi-level composable sound functions for expressive and compact sound description.
import {$, sin, adsr, note} from 'unisons'
sin(note.A4 + sin(5, 10)) * adsr(0, 0, .4, 0) | speaker()
// === zzfx.play(...[,0,925,,.4,0,,,,,,,,,5]);
Main goals:
- expressing complex sounds with tiny footprint (serializable to string);
- reproduce 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 and descriptive form with operators overloading.
Examples
// zzfx(...[,,1675,,.06,.24,1,1.82,,,837,.06]); // coin
tri(t => 1675 + ( t > 0.06 ? 837 : 0 )) * adsr(0,0,.06,.24) | shape(1.82) | speaker()
API
import { src, osc, vol, sin, adsr, $ } from 'sones'
// define sound expression
$(440) // constant signal
$() // silence
$(a + b + c) // sound expression (see below)
$(time => value) // generate from time function, null = end
// oscillators
sin(f), tri(f), saw(f), rect(f), tan(f) // params: f, duration, amp
osc(seed, freq=220) // oscillate periodic waveform, eg. o(4321) === ac.createPeriodicWave([1,.75,.5,.25])
// noises
white(), purple(), brown(), gray()
// randoms
rand(from, to)
gauss(mean, sdev)
choice(arr)
// sources
src('https://url.to/my-sound.mp3') // remote/local source
src('base64') // base64 source
from(new Uint8Array([2,5,123,...])) // uint8 samples 0..255
from(new Float32Array([.1, .25, .43, ...])) // float samples -1..+1
mic(vol | params) // signal from microphone
waa(node) // signal from web audio api node
midi()
// mixing
mix(a, b, c, ...) // sum + normalize signals
seq(a, b, c, ...) // sequence of signals
// mux([a, b, c], selector) // multiplexor, unlike sampler, selects input signal
// NOTE: in fact overloading can be employed to provide these ops
// transitions (useful to multiply source)
line(from, to)
fade(from, to)
adsr(a, d?, s?, r?)
// windows
blackman()
rect()
// ZZFX adapter
zzfx(...params)
// transforms
vol(amp) // multiply signal
// gain(amp) // multiply signal in decibels
dur(t) // limit signal by the duration
map((x, t) => y) // map samples
// filters
lp(f, Q)
hp(f, Q)
bp(f, Q)
ls()
hs()
peak()
notch()
allpass()
biquad()
loudness()
// fx
shape(curve, oversample) // waveshaper node / zzfx curve
delay(t=0, mix=1) // zzfx delay, or pitch jump: delay(1)
reverb(profile)
repeat(frame) // zzfx repeat
compress()
// audio
pan(-1..+1) // direct sound to one of the channels
// musical
note(n='A4'), note`A4`, note.A4
chord(type, root)
sampler(dict, controller)
// output
speaker(amp=1)
file(name)
array(arr)
// plotting: waveform=1, spectrum=2, spectrorgam=3
plot(type=1, options)
// primitives
10 // static number
[10, 20, 30] // multiple signals in one to mix? channels? sequence?
B1, A3 // scientific notation notes
// operators
sin(220) + sin(440) - sin(880) + .5 // add/subtract signals + constant shift
sin(440) * adsr(.5, .2, .1, .1) * .5 // multiply: modulate amplitude, amplify
sin(440) | shape(.4) | speaker(.75) // pipe signals
Operator overloading
Unisons support limited operator overloading expressions:
Expression | Limit | Good | Bad | Solution |
---|---|---|---|---|
±a ± b? ± c?... ± number | number < 100000 | sin(220) + sin(440) + .5 | sin(220) + sin(440) + sin(880) + 192000 | use mix to merge 2+ signals and big constants: mix(sin(220), sin(440), sin(880), 192000) |
±a * b? * c? * number | 0.001 < number < 1000 | sin(440) * .001 | sin(440) * .0001 | use vol for precise amp: sin(440) | vol(.1234) |
±a * m ± b? * n? + number | coef is known rational fraction < 10, number < 100000 | sin(220)/3 + sin(440)/3 + 1/3 | sin(220)*.171 + sin(440)*.231 + .741 | use map for arbitrary calc sin(440) | map(x => x*.171 + 1) or mix to precise weighting mix(sin(220)*.171, sin(440)*.231, .741) |
a | b | c ... | b , c are transforms; pipe loses static constant shift part of expression; pipe must be wrapped into $() or connected to output. | white() | lpf(440) | speaker() | sin(440) * .5 + .5 | lpf() |
Motivation
- Slang: we don't need new language, we have JS.
- ZZFX: params are unreadable, can be replaced with fn composition.
- WAA: hairy.
- Wasgen: wraps WAA.
- Soundfunts: heavy.