jevstrudel.git / packages / superdough / zzfx_fork.mjs
1import { getAudioContext } from './audioContext.mjs';

https://github.com/KilledByAPixel/ZzFX/blob/master/ZzFX.js#L85C5-L180C6 changes: replaced this.volume with 1 + using sampleRate from getAudioContext()

5export function buildSamples(
6  volume = 1,
7  randomness = 0.05,
8  frequency = 220,
9  attack = 0,
10  sustain = 0,
11  release = 0.1,
12  shape = 0,
13  shapeCurve = 1,
14  slide = 0,
15  deltaSlide = 0,
16  pitchJump = 0,
17  pitchJumpTime = 0,
18  repeatTime = 0,
19  noise = 0,
20  modulation = 0,
21  bitCrush = 0,
22  delay = 0,
23  sustainVolume = 1,
24  decay = 0,
25  tremolo = 0,
26) {
27  // init parameters
28  let PI2 = Math.PI * 2,
29    sampleRate = getAudioContext().sampleRate,
30    sign = (v) => (v > 0 ? 1 : -1),
31    startSlide = (slide *= (500 * PI2) / sampleRate / sampleRate),
32    startFrequency = (frequency *= ((1 + randomness * 2 * Math.random() - randomness) * PI2) / sampleRate),
33    b = [],
34    t = 0,
35    tm = 0,
36    i = 0,
37    j = 1,
38    r = 0,
39    c = 0,
40    s = 0,
41    f,
42    length;

scale by sample rate

45  attack = attack * sampleRate + 9; // minimum attack to prevent pop
46  decay *= sampleRate;
47  sustain *= sampleRate;
48  release *= sampleRate;
49  delay *= sampleRate;
50  deltaSlide *= (500 * PI2) / sampleRate ** 3;
51  modulation *= PI2 / sampleRate;
52  pitchJump *= PI2 / sampleRate;
53  pitchJumpTime *= sampleRate;
54  repeatTime = (repeatTime * sampleRate) | 0;

generate waveform

57  for (length = (attack + decay + sustain + release + delay) | 0; i < length; b[i++] = s) {
58    if (!(++c % ((bitCrush * 100) | 0))) {
59      // bit crush
60      s = shape
61        ? shape > 1
62          ? shape > 2
63            ? shape > 3 // wave shape
64              ? Math.sin((t % PI2) ** 3) // 4 noise
65              : Math.max(Math.min(Math.tan(t), 1), -1) // 3 tan
66            : 1 - (((((2 * t) / PI2) % 2) + 2) % 2) // 2 saw
67          : 1 - 4 * Math.abs(Math.round(t / PI2) - t / PI2) // 1 triangle
68        : Math.sin(t); // 0 sin
69
70      s =
71        (repeatTime
72          ? 1 - tremolo + tremolo * Math.sin((PI2 * i) / repeatTime) // tremolo
73          : 1) *
74        sign(s) *
75        Math.abs(s) ** shapeCurve * // curve 0=square, 2=pointy
76        volume *
77        1 * // envelope
78        (i < attack
79          ? i / attack // attack
80          : i < attack + decay // decay
81            ? 1 - ((i - attack) / decay) * (1 - sustainVolume) // decay falloff
82            : i < attack + decay + sustain // sustain
83              ? sustainVolume // sustain volume
84              : i < length - delay // release
85                ? ((length - i - delay) / release) * // release falloff
86                  sustainVolume // release volume
87                : 0); // post release
88
89      s = delay
90        ? s / 2 +
91          (delay > i
92            ? 0 // delay
93            : ((i < length - delay ? 1 : (length - i) / delay) * // release delay
94                b[(i - delay) | 0]) /
95              2)
96        : s; // sample delay
97    }
98
99    f =
100      (frequency += slide += deltaSlide) * // frequency
101      Math.cos(modulation * tm++); // modulation
102    t += f - f * noise * (1 - (((Math.sin(i) + 1) * 1e9) % 2)); // noise
103
104    if (j && ++j > pitchJumpTime) {
105      // pitch jump
106      frequency += pitchJump; // apply pitch jump
107      startFrequency += pitchJump; // also apply to start
108      j = 0; // stop pitch jump time
109    }
110
111    if (repeatTime && !(++r % repeatTime)) {
112      // repeat
113      frequency = startFrequency; // reset frequency
114      slide = startSlide; // reset slide
115      j ||= 1; // reset pitch jump time
116    }
117  }
119  return b;
120}