-
Notifications
You must be signed in to change notification settings - Fork 37
/
ZzFXMicro.js
131 lines (114 loc) · 5.32 KB
/
ZzFXMicro.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// ZzFX - Zuper Zmall Zound Zynth - Micro Edition
// MIT License - Copyright 2019 Frank Force
// https://github.com/KilledByAPixel/ZzFX
// This is a minified build of zzfx for use in size coding projects.
// You can use zzfxV to set volume.
// Feel free to minify it further for your own needs!
'use strict';
///////////////////////////////////////////////////////////////////////////////
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.1 by Frank Force
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name ZzFXMicro.min.js
// @js_externs zzfx, zzfxG, zzfxP, zzfxV, zzfxX
// @language_out ECMASCRIPT_2019
// ==/ClosureCompiler==
const zzfx = (...z)=> zzfxP(zzfxG(...z)); // generate and play sound
const zzfxV = .3; // volume
const zzfxR = 44100; // sample rate
const zzfxX = new AudioContext; // audio context
const zzfxP = (...samples)=> // play samples
{
// create buffer and source
let buffer = zzfxX.createBuffer(samples.length, samples[0].length, zzfxR),
source = zzfxX.createBufferSource();
// copy samples to buffer and play
samples.map((d,i)=> buffer.getChannelData(i).set(d));
source.buffer = buffer;
source.connect(zzfxX.destination);
source.start();
return source;
}
const zzfxG = // generate samples
(
// parameters
volume = 1, randomness = .05, frequency = 220, attack = 0, sustain = 0,
release = .1, shape = 0, shapeCurve = 1, slide = 0, deltaSlide = 0,
pitchJump = 0, pitchJumpTime = 0, repeatTime = 0, noise = 0, modulation = 0,
bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0, filter = 0
)=>
{
// init parameters
let PI2 = Math.PI*2, sign = v => v<0?-1:1,
startSlide = slide *= 500 * PI2 / zzfxR / zzfxR,
startFrequency = frequency *=
(1 + randomness*2*Math.random() - randomness) * PI2 / zzfxR,
b=[], t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f, length,
// biquad LP/HP filter
quality = 2, w = PI2 * Math.abs(filter) * 2 / zzfxR,
cos = Math.cos(w), alpha = Math.sin(w) / 2 / quality,
a0 = 1 + alpha, a1 = -2*cos / a0, a2 = (1 - alpha) / a0,
b0 = (1 + sign(filter) * cos) / 2 / a0,
b1 = -(sign(filter) + cos) / a0, b2 = b0,
x2 = 0, x1 = 0, y2 = 0, y1 = 0;
// scale by sample rate
attack = attack * zzfxR + 9; // minimum attack to prevent pop
decay *= zzfxR;
sustain *= zzfxR;
release *= zzfxR;
delay *= zzfxR;
deltaSlide *= 500 * PI2 / zzfxR**3;
modulation *= PI2 / zzfxR;
pitchJump *= PI2 / zzfxR;
pitchJumpTime *= zzfxR;
repeatTime = repeatTime * zzfxR | 0;
volume *= zzfxV;
// generate waveform
for(length = attack + decay + sustain + release + delay | 0;
i < length; b[i++] = s * volume) // sample
{
if (!(++c%(bitCrush*100|0))) // bit crush
{
s = shape? shape>1? shape>2? shape>3? // wave shape
Math.sin(t**3) : // 4 noise
Math.max(Math.min(Math.tan(t),1),-1): // 3 tan
1-(2*t/PI2%2+2)%2: // 2 saw
1-4*Math.abs(Math.round(t/PI2)-t/PI2): // 1 triangle
Math.sin(t); // 0 sin
s = (repeatTime ?
1 - tremolo + tremolo*Math.sin(PI2*i/repeatTime) // tremolo
: 1) *
sign(s)*(Math.abs(s)**shapeCurve) * // curve
(i < attack ? i/attack : // attack
i < attack + decay ? // decay
1-((i-attack)/decay)*(1-sustainVolume) : // decay falloff
i < attack + decay + sustain ? // sustain
sustainVolume : // sustain volume
i < length - delay ? // release
(length - i - delay)/release * // release falloff
sustainVolume : // release volume
0); // post release
s = delay ? s/2 + (delay > i ? 0 : // delay
(i<length-delay? 1 : (length-i)/delay) * // release delay
b[i-delay|0]/2/volume) : s; // sample delay
if (filter) // apply filter
s = y1 = b2*x2 + b1*(x2=x1) + b0*(x1=s) - a2*y2 - a1*(y2=y1);
}
f = (frequency += slide += deltaSlide) *// frequency
Math.cos(modulation*tm++); // modulation
t += f + f*noise*Math.sin(i**5); // noise
if (j && ++j > pitchJumpTime) // pitch jump
{
frequency += pitchJump; // apply pitch jump
startFrequency += pitchJump; // also apply to start
j = 0; // stop pitch jump time
}
if (repeatTime && !(++r % repeatTime)) // repeat
{
frequency = startFrequency; // reset frequency
slide = startSlide; // reset slide
j = j || 1; // reset pitch jump time
}
}
return b;
}