fix: use custom version of ZZFX

- ZZFX audio context broke tests
- only needed buildSamples function
This commit is contained in:
Felix Roos 2023-08-31 03:50:38 +02:00
parent 90cdea3656
commit b991cfc493
4 changed files with 127 additions and 15 deletions

View File

@ -161,5 +161,6 @@ Then just make sure your first call of `superdough` happens after a click of som
## Credits
- [ZZFX](https://github.com/KilledByAPixel/ZzFX) used for synths starting with z
- [SuperDirt](https://github.com/musikinformatik/SuperDirt)
- [WebDirt](https://github.com/dktr0/WebDirt)

View File

@ -36,7 +36,6 @@
"vite": "^4.3.3"
},
"dependencies": {
"nanostores": "^0.8.1",
"zzfx": "^1.2.0"
"nanostores": "^0.8.1"
}
}

View File

@ -1,4 +1,4 @@
import { ZZFX } from 'zzfx';
//import { ZZFX } from 'zzfx';
import { midiToFreq, noteToMidi } from './util.mjs';
import { registerSound, getAudioContext } from './superdough.mjs';
@ -82,7 +82,7 @@ export const getZZFX = (value, t, duration) => {
const readableParams = Object.fromEntries(paramOrder.map((param, i) => [param, params[i]]));
console.log(readableParams);
const samples = ZZFX.buildSamples(...params);
const samples = /* ZZFX. */ buildSamples(...params);
const context = getAudioContext();
const buffer = context.createBuffer(1, samples.length, context.sampleRate);
buffer.getChannelData(0).set(samples);
@ -98,7 +98,7 @@ export function registerZZFXSounds() {
console.log('registerZZFXSounds');
['zsine', 'zsaw', 'ztri', 'ztan', 'znoise'].forEach((wave) => {
registerSound(wave, (t, value, onended) => {
const duration = 0.2;
const duration = 0.3;
const { node: o } = getZZFX({ s: wave, ...value }, t, duration);
o.onended = () => {
o.disconnect();
@ -111,3 +111,122 @@ export function registerZZFXSounds() {
});
});
}
// https://github.com/KilledByAPixel/ZzFX/blob/master/ZzFX.js#L85C5-L180C6
// changes: replaced this.volume with 1 + using sampleRate from getAudioContext()
function buildSamples(
volume = 1,
randomness = 0.05,
frequency = 220,
attack = 0,
sustain = 0,
release = 0.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,
) {
// init parameters
let PI2 = Math.PI * 2,
sampleRate = getAudioContext().sampleRate,
sign = (v) => (v > 0 ? 1 : -1),
startSlide = (slide *= (500 * PI2) / sampleRate / sampleRate),
startFrequency = (frequency *= ((1 + randomness * 2 * Math.random() - randomness) * PI2) / sampleRate),
b = [],
t = 0,
tm = 0,
i = 0,
j = 1,
r = 0,
c = 0,
s = 0,
f,
length;
// scale by sample rate
attack = attack * sampleRate + 9; // minimum attack to prevent pop
decay *= sampleRate;
sustain *= sampleRate;
release *= sampleRate;
delay *= sampleRate;
deltaSlide *= (500 * PI2) / sampleRate ** 3;
modulation *= PI2 / sampleRate;
pitchJump *= PI2 / sampleRate;
pitchJumpTime *= sampleRate;
repeatTime = (repeatTime * sampleRate) | 0;
// generate waveform
for (length = (attack + decay + sustain + release + delay) | 0; i < length; b[i++] = s) {
if (!(++c % ((bitCrush * 100) | 0))) {
// bit crush
s = shape
? shape > 1
? shape > 2
? shape > 3 // wave shape
? Math.sin((t % PI2) ** 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 0=square, 2=pointy
volume *
1 * // envelope
(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)
: s; // sample delay
}
f =
(frequency += slide += deltaSlide) * // frequency
Math.cos(modulation * tm++); // modulation
t += f - f * noise * (1 - (((Math.sin(i) + 1) * 1e9) % 2)); // 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 ||= 1; // reset pitch jump time
}
}
return b;
}

13
pnpm-lock.yaml generated
View File

@ -1,9 +1,5 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
@ -395,9 +391,6 @@ importers:
nanostores:
specifier: ^0.8.1
version: 0.8.1
zzfx:
specifier: ^1.2.0
version: 1.2.0
devDependencies:
vite:
specifier: ^4.3.3
@ -14038,6 +14031,6 @@ packages:
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
/zzfx@1.2.0:
resolution: {integrity: sha512-RtFz6PTMfCmxTfaCOv6GWAV4YaL/T0hltiMGkd87clybO8WLPlH6kX8sNkZGFKw9YPyu1UNsUYf/5/Vn4dondA==}
dev: false
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false