rewrite webaudio + migrate tunes + empty setters
This commit is contained in:
parent
f69d776cf6
commit
4c05876066
4 changed files with 125 additions and 160 deletions
|
|
@ -6,21 +6,20 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||
|
||||
// import { Pattern, getFrequency, patternify2 } from '@strudel.cycles/core';
|
||||
import * as strudel from '@strudel.cycles/core';
|
||||
import { fromMidi } from '@strudel.cycles/core';
|
||||
import { Tone } from '@strudel.cycles/tone';
|
||||
const { Pattern, getFrequency, patternify2 } = strudel;
|
||||
const { Pattern } = strudel;
|
||||
|
||||
let audioContext;
|
||||
export const getAudioContext = () => {
|
||||
if (!audioContext) {
|
||||
audioContext = new AudioContext();
|
||||
}
|
||||
return audioContext;
|
||||
const getFilter = (ac, type, frequency, Q) => {
|
||||
const filter = ac.createBiquadFilter();
|
||||
filter.type = type;
|
||||
filter.frequency.value = frequency;
|
||||
filter.Q.value = Q;
|
||||
return filter;
|
||||
};
|
||||
|
||||
const lookahead = 0.2;
|
||||
|
||||
const adsr = (attack, decay, sustain, release, velocity, begin, end) => {
|
||||
const gainNode = getAudioContext().createGain();
|
||||
const getADSR = (ac, attack, decay, sustain, release, velocity, begin, end) => {
|
||||
const gainNode = ac.createGain();
|
||||
gainNode.gain.setValueAtTime(0, begin);
|
||||
gainNode.gain.linearRampToValueAtTime(velocity, begin + attack); // attack
|
||||
gainNode.gain.linearRampToValueAtTime(sustain * velocity, begin + attack + decay); // sustain start
|
||||
|
|
@ -30,129 +29,70 @@ const adsr = (attack, decay, sustain, release, velocity, begin, end) => {
|
|||
return gainNode;
|
||||
};
|
||||
|
||||
Pattern.prototype.withAudioNode = function (createAudioNode) {
|
||||
return this._withHap((hap) => {
|
||||
return hap.setContext({
|
||||
...hap.context,
|
||||
createAudioNode: (t, e) => createAudioNode(t, e, hap.context.createAudioNode?.(t, hap)),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Pattern.prototype._wave = function (type) {
|
||||
return this.withAudioNode((t, e) => {
|
||||
const osc = getAudioContext().createOscillator();
|
||||
osc.type = type;
|
||||
const f = getFrequency(e);
|
||||
osc.frequency.value = f; // expects frequency..
|
||||
const begin = t ?? e.whole.begin.valueOf() + lookahead;
|
||||
const end = begin + e.duration.valueOf();
|
||||
osc.start(begin);
|
||||
osc.stop(end); // release?
|
||||
return osc;
|
||||
});
|
||||
};
|
||||
Pattern.prototype.adsr = function (a = 0.01, d = 0.05, s = 1, r = 0.01) {
|
||||
return this.withAudioNode((t, e, node) => {
|
||||
const velocity = e.context?.velocity || 1;
|
||||
const begin = t ?? e.whole.begin.valueOf() + lookahead;
|
||||
const end = begin + e.duration.valueOf() + lookahead;
|
||||
const envelope = adsr(a, d, s, r, velocity, begin, end);
|
||||
node?.connect(envelope);
|
||||
return envelope;
|
||||
});
|
||||
};
|
||||
Pattern.prototype._filter = function (type = 'lowpass', frequency = 1000) {
|
||||
return this.withAudioNode((t, e, node) => {
|
||||
const filter = getAudioContext().createBiquadFilter();
|
||||
filter.type = type;
|
||||
filter.frequency.value = frequency;
|
||||
node?.connect(filter);
|
||||
return filter;
|
||||
});
|
||||
};
|
||||
|
||||
Pattern.prototype.filter = function (type, frequency) {
|
||||
return patternify2(Pattern.prototype._filter)(reify(type), reify(frequency), this);
|
||||
};
|
||||
|
||||
Pattern.prototype.out = function () {
|
||||
const master = getAudioContext().createGain();
|
||||
master.gain.value = 0.1;
|
||||
master.connect(getAudioContext().destination);
|
||||
return this.withAudioNode((t, e, node) => {
|
||||
if (!node) {
|
||||
console.warn('out: no source! call .osc() first');
|
||||
return this.onTrigger((t, hap, ct) => {
|
||||
const ac = Tone.getContext().rawContext;
|
||||
// calculate correct time (tone.js workaround)
|
||||
t = ac.currentTime + t - ct;
|
||||
// destructure value
|
||||
let {
|
||||
freq,
|
||||
s,
|
||||
n,
|
||||
gain = 1,
|
||||
cutoff,
|
||||
resonance = 1,
|
||||
hcutoff,
|
||||
hresonance = 1,
|
||||
bandf,
|
||||
bandq = 1,
|
||||
pan,
|
||||
attack = 0.001,
|
||||
decay = 0,
|
||||
sustain = 1,
|
||||
release = 0.001,
|
||||
} = hap.value;
|
||||
if (!n && !freq) {
|
||||
console.warn('unplayable value:', hap.value);
|
||||
return;
|
||||
}
|
||||
node?.connect(master);
|
||||
})._withHap((hap) => {
|
||||
const onTrigger = (time, e) => e.context?.createAudioNode?.(time, e);
|
||||
return hap.setContext({ ...hap.context, onTrigger });
|
||||
// get frequency
|
||||
if (!freq && typeof n === 'number') {
|
||||
freq = fromMidi(n); // + 48);
|
||||
}
|
||||
if (!freq && typeof n === 'string') {
|
||||
freq = fromMidi(toMidi(n));
|
||||
}
|
||||
// the chain will hold all audio nodes that connect to each other
|
||||
const chain = [];
|
||||
// make oscillator
|
||||
const o = ac.createOscillator();
|
||||
o.type = s || 'triangle';
|
||||
o.frequency.value = Number(freq);
|
||||
o.start(t);
|
||||
o.stop(t + hap.duration + release);
|
||||
chain.push(o);
|
||||
// envelope
|
||||
const adsr = getADSR(ac, attack, decay, sustain, release, 1, t, t + hap.duration);
|
||||
chain.push(adsr);
|
||||
// filters
|
||||
cutoff !== undefined && chain.push(getFilter(ac, 'lowpass', cutoff, resonance));
|
||||
hcutoff !== undefined && chain.push(getFilter(ac, 'highpass', hcutoff, hresonance));
|
||||
bandf !== undefined && chain.push(getFilter(ac, 'bandpass', bandf, bandq));
|
||||
// TODO vowel
|
||||
// TODO delay / delaytime / delayfeedback
|
||||
// panning
|
||||
if (pan !== undefined) {
|
||||
const panner = ac.createStereoPanner();
|
||||
panner.pan.value = 2 * pan - 1;
|
||||
chain.push(panner);
|
||||
}
|
||||
// master out
|
||||
const master = ac.createGain();
|
||||
master.gain.value = 0.1 * gain;
|
||||
chain.push(master);
|
||||
chain.push(ac.destination);
|
||||
// connect chain elements together
|
||||
chain.slice(1).reduce((last, current) => last.connect(current), chain[0]);
|
||||
});
|
||||
};
|
||||
|
||||
Pattern.prototype.define('wave', (type, pat) => pat.wave(type), { patternified: true });
|
||||
|
||||
/*
|
||||
|
||||
// TODO: throw all this away and use that:
|
||||
|
||||
stack(
|
||||
freq("55 [110,165] 110 [220,275]".mul("<1 <3/4 2/3>>").struct("x(3,8)")
|
||||
.layer(x=>x.mul("1.006,.995"))), // detune
|
||||
freq("440(5,8)".legato(.18).mul("<1 3/4 2 2/3>")).gain(perlin.range(.2,.8))
|
||||
).s("<sawtooth square>/2")
|
||||
.cutoff(perlin.range(100,4000).slow(4))//.resonance(saw.range(0,15).slow(7))
|
||||
.jux(rev)
|
||||
.onTrigger((t,hap,ct) => {
|
||||
const ac = Tone.getContext().rawContext;
|
||||
// calculate correct time (tone.js workaround)
|
||||
t = ac.currentTime + t - ct;
|
||||
// destructure value
|
||||
const { freq, s, gain = 1,
|
||||
cutoff, resonance = 1,
|
||||
hcutoff, hresonance = 1,
|
||||
bandf, bandq = 1,
|
||||
pan
|
||||
} = hap.value;
|
||||
// TODO get frequency from n or note
|
||||
// oscillator
|
||||
const o = ac.createOscillator();
|
||||
o.type = s || 'triangle';
|
||||
o.frequency.value = Number(freq);
|
||||
o.start(t);
|
||||
o.stop(t + hap.duration);
|
||||
// chaining logic
|
||||
let last = o;
|
||||
const addToChain = (node) => {
|
||||
last.connect(node);
|
||||
last = node;
|
||||
}
|
||||
// filters
|
||||
const getFilter = (type, frequency, Q) => {
|
||||
const filter = ac.createBiquadFilter();
|
||||
filter.type = type;
|
||||
filter.frequency.value = frequency;
|
||||
filter.Q.value = Q;
|
||||
return filter;
|
||||
}
|
||||
cutoff !== undefined && addToChain(getFilter('lowpass', cutoff, resonance));
|
||||
hcutoff !== undefined && addToChain(getFilter('highpass', hcutoff, hresonance));
|
||||
bandf !== undefined && addToChain(getFilter('bandpass', bandf, bandq));
|
||||
// TODO: vowel
|
||||
// TODO: delay / delaytime / delayfeedback
|
||||
// panning
|
||||
if(pan !== undefined) {
|
||||
const panner = ac.createStereoPanner();
|
||||
panner.pan.value = 2*pan-1;
|
||||
addToChain(panner);
|
||||
}
|
||||
// gain out
|
||||
const master = ac.createGain();
|
||||
master.gain.value = 0.1 * gain;
|
||||
master.connect(ac.destination);
|
||||
last.connect(master);
|
||||
})
|
||||
.stack(s("bd(3,8),hh*4,~ sd").webdirt())
|
||||
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue