mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 21:58:37 +00:00
159 lines
6.2 KiB
JavaScript
159 lines
6.2 KiB
JavaScript
import {Pattern as _Pattern} from "../_snowpack/link/strudel.js";
|
|
import {
|
|
AutoFilter,
|
|
Destination,
|
|
Filter,
|
|
Gain,
|
|
isNote,
|
|
Synth,
|
|
PolySynth,
|
|
MembraneSynth,
|
|
MetalSynth,
|
|
MonoSynth,
|
|
AMSynth,
|
|
DuoSynth,
|
|
FMSynth,
|
|
NoiseSynth,
|
|
PluckSynth,
|
|
Sampler
|
|
} from "../_snowpack/pkg/tone.js";
|
|
const Pattern = _Pattern;
|
|
Pattern.prototype.tone = function(instrument) {
|
|
return this.fmap((value) => {
|
|
value = typeof value !== "object" && !Array.isArray(value) ? {value} : value;
|
|
const onTrigger = (time, event) => {
|
|
if (instrument.constructor.name === "PluckSynth") {
|
|
instrument.triggerAttack(value.value, time);
|
|
} else if (instrument.constructor.name === "NoiseSynth") {
|
|
instrument.triggerAttackRelease(event.duration, time);
|
|
} else {
|
|
instrument.triggerAttackRelease(value.value, event.duration, time);
|
|
}
|
|
};
|
|
return {...value, instrument, onTrigger};
|
|
});
|
|
};
|
|
Pattern.prototype.define("tone", (type, pat) => pat.tone(type), {composable: true, patternified: false});
|
|
export const amsynth = (options) => new AMSynth(options);
|
|
export const duosynth = (options) => new DuoSynth(options);
|
|
export const fmsynth = (options) => new FMSynth(options);
|
|
export const membrane = (options) => new MembraneSynth(options);
|
|
export const metal = (options) => new MetalSynth(options);
|
|
export const monosynth = (options) => new MonoSynth(options);
|
|
export const noise = (options) => new NoiseSynth(options);
|
|
export const pluck = (options) => new PluckSynth(options);
|
|
export const polysynth = (options) => new PolySynth(options);
|
|
export const sampler = (options) => new Sampler(options);
|
|
export const synth = (options) => new Synth(options);
|
|
export const vol = (v) => new Gain(v);
|
|
export const lowpass = (v) => new Filter(v, "lowpass");
|
|
export const highpass = (v) => new Filter(v, "highpass");
|
|
export const adsr = (a, d = 0.1, s = 0.4, r = 0.01) => ({envelope: {attack: a, decay: d, sustain: s, release: r}});
|
|
export const osc = (type) => ({oscillator: {type}});
|
|
export const out = Destination;
|
|
const chainable = function(instr) {
|
|
const _chain = instr.chain.bind(instr);
|
|
let chained = [];
|
|
instr.chain = (...args) => {
|
|
chained = chained.concat(args);
|
|
instr.disconnect();
|
|
return _chain(...chained, Destination);
|
|
};
|
|
instr.filter = (freq = 1e3, type = "lowpass") => instr.chain(new Filter(freq, type));
|
|
instr.gain = (gain2 = 0.9) => instr.chain(new Gain(gain2));
|
|
return instr;
|
|
};
|
|
export const poly = (type) => {
|
|
const s = new PolySynth(Synth, {oscillator: {type}}).toDestination();
|
|
return chainable(s);
|
|
};
|
|
Pattern.prototype._poly = function(type = "triangle") {
|
|
const instrumentConfig = {
|
|
oscillator: {type},
|
|
envelope: {attack: 0.01, decay: 0.01, sustain: 0.6, release: 0.01}
|
|
};
|
|
if (!this.instrument) {
|
|
this.instrument = poly(type);
|
|
}
|
|
return this.fmap((value) => {
|
|
value = typeof value !== "object" && !Array.isArray(value) ? {value} : value;
|
|
const onTrigger = (time, event) => {
|
|
this.instrument.set(instrumentConfig);
|
|
this.instrument.triggerAttackRelease(value.value, event.duration, time);
|
|
};
|
|
return {...value, instrumentConfig, onTrigger};
|
|
});
|
|
};
|
|
Pattern.prototype.define("poly", (type, pat) => pat.poly(type), {composable: true, patternified: true});
|
|
const getTrigger = (getChain, value) => (time, event) => {
|
|
const chain = getChain();
|
|
if (!isNote(value)) {
|
|
throw new Error("not a note: " + value);
|
|
}
|
|
chain.triggerAttackRelease(value, event.duration, time);
|
|
setTimeout(() => {
|
|
chain.dispose();
|
|
}, event.duration * 2e3);
|
|
};
|
|
Pattern.prototype._synth = function(type = "triangle") {
|
|
return this.fmap((value) => {
|
|
value = typeof value !== "object" && !Array.isArray(value) ? {value} : value;
|
|
const instrumentConfig = {
|
|
oscillator: {type},
|
|
envelope: {attack: 0.01, decay: 0.01, sustain: 0.6, release: 0.01}
|
|
};
|
|
const getInstrument = () => {
|
|
const instrument = new Synth();
|
|
instrument.set(instrumentConfig);
|
|
return instrument;
|
|
};
|
|
const onTrigger = getTrigger(() => getInstrument().toDestination(), value.value);
|
|
return {...value, getInstrument, instrumentConfig, onTrigger};
|
|
});
|
|
};
|
|
Pattern.prototype.adsr = function(attack = 0.01, decay = 0.01, sustain = 0.6, release = 0.01) {
|
|
return this.fmap((value) => {
|
|
if (!value?.getInstrument) {
|
|
throw new Error("cannot chain adsr: need instrument first (like synth)");
|
|
}
|
|
const instrumentConfig = {...value.instrumentConfig, envelope: {attack, decay, sustain, release}};
|
|
const getInstrument = () => {
|
|
const instrument = value.getInstrument();
|
|
instrument.set(instrumentConfig);
|
|
return instrument;
|
|
};
|
|
const onTrigger = getTrigger(() => getInstrument().toDestination(), value.value);
|
|
return {...value, getInstrument, instrumentConfig, onTrigger};
|
|
});
|
|
};
|
|
Pattern.prototype.chain = function(...effectGetters) {
|
|
return this.fmap((value) => {
|
|
if (!value?.getInstrument) {
|
|
throw new Error("cannot chain: need instrument first (like synth)");
|
|
}
|
|
const chain = (value.chain || []).concat(effectGetters);
|
|
const getChain = () => {
|
|
const effects = chain.map((getEffect) => getEffect());
|
|
return value.getInstrument().chain(...effects, Destination);
|
|
};
|
|
const onTrigger = getTrigger(getChain, value.value);
|
|
return {...value, getChain, onTrigger, chain};
|
|
});
|
|
};
|
|
export const autofilter = (freq = 1) => () => new AutoFilter(freq).start();
|
|
export const filter = (freq = 1, q = 1, type = "lowpass") => () => new Filter(freq, type);
|
|
export const gain = (gain2 = 0.9) => () => new Gain(gain2);
|
|
Pattern.prototype._gain = function(g) {
|
|
return this.chain(gain(g));
|
|
};
|
|
Pattern.prototype._filter = function(freq, q, type = "lowpass") {
|
|
return this.chain(filter(freq, q, type));
|
|
};
|
|
Pattern.prototype._autofilter = function(g) {
|
|
return this.chain(autofilter(g));
|
|
};
|
|
Pattern.prototype.define("synth", (type, pat) => pat.synth(type), {composable: true, patternified: true});
|
|
Pattern.prototype.define("gain", (gain2, pat) => pat.synth(gain2), {composable: true, patternified: true});
|
|
Pattern.prototype.define("filter", (cutoff, pat) => pat.filter(cutoff), {composable: true, patternified: true});
|
|
Pattern.prototype.define("autofilter", (cutoff, pat) => pat.filter(cutoff), {composable: true, patternified: true});
|