mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-13 14:48:32 +00:00
add .tone + helpers
This commit is contained in:
parent
71023fc09c
commit
70f858361f
@ -62,7 +62,8 @@ function scaleTranspose(scale: string, offset: number, note: string) {
|
||||
Pattern.prototype._mapNotes = function (func: (note: NoteEvent) => NoteEvent) {
|
||||
return this.fmap((event: string | NoteEvent) => {
|
||||
const noteEvent = toNoteEvent(event);
|
||||
return func(noteEvent);
|
||||
// TODO: generalize? this is practical for any event that is expected to be an object with
|
||||
return { ...noteEvent, ...func(noteEvent) };
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -1,8 +1,98 @@
|
||||
import { Pattern as _Pattern } from '../../strudel.mjs';
|
||||
import { AutoFilter, Destination, Filter, Gain, isNote, Synth } from 'tone';
|
||||
import { AutoFilter, Destination, Filter, Gain, isNote, Synth, PolySynth } from 'tone';
|
||||
|
||||
const Pattern = _Pattern as any;
|
||||
|
||||
// with this function, you can play the pattern with any tone synth
|
||||
Pattern.prototype.tone = function (instrument) {
|
||||
// instrument.toDestination();
|
||||
return this.fmap((value: any) => {
|
||||
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); // noise has no value
|
||||
} 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 });
|
||||
|
||||
// helpers
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
|
||||
You are entering experimental zone
|
||||
|
||||
*/
|
||||
|
||||
// the following code is an attempt to minimize tonejs code.. it is still an experiment
|
||||
|
||||
const chainable = function (instr) {
|
||||
const _chain = instr.chain.bind(instr);
|
||||
let chained: any = [];
|
||||
instr.chain = (...args) => {
|
||||
chained = chained.concat(args);
|
||||
instr.disconnect(); // disconnect from destination / previous chain
|
||||
return _chain(...chained, Destination);
|
||||
};
|
||||
// shortcuts: chaining multiple won't work forn now.. like filter(1000).gain(0.5). use chain + native Tone calls instead
|
||||
instr.filter = (freq = 1000, type: BiquadFilterType = 'lowpass') =>
|
||||
instr.chain(
|
||||
new Filter(freq, type) // .Q.setValueAtTime(q, time);
|
||||
);
|
||||
instr.gain = (gain: number = 0.9) => instr.chain(new Gain(gain));
|
||||
return instr;
|
||||
};
|
||||
|
||||
// helpers
|
||||
export const poly = (type) => {
|
||||
const s: any = new PolySynth(Synth, { oscillator: { type } }).toDestination();
|
||||
return chainable(s);
|
||||
};
|
||||
|
||||
Pattern.prototype._poly = function (type: any = 'triangle') {
|
||||
const instrumentConfig: any = {
|
||||
oscillator: { type },
|
||||
envelope: { attack: 0.01, decay: 0.01, sustain: 0.6, release: 0.01 },
|
||||
};
|
||||
if (!this.instrument) {
|
||||
// create only once to keep the js heap happy
|
||||
// this.instrument = new PolySynth(Synth, instrumentConfig).toDestination();
|
||||
this.instrument = poly(type);
|
||||
}
|
||||
return this.fmap((value: any) => {
|
||||
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 });
|
||||
|
||||
/*
|
||||
|
||||
You are entering danger zone
|
||||
|
||||
*/
|
||||
|
||||
// everything below is nice in theory, but not healthy for the JS heap, as nodes get recreated on every call
|
||||
|
||||
const getTrigger = (getChain: any, value: any) => (time: number, event: any) => {
|
||||
const chain = getChain(); // make sure this returns a node that is connected toDestination // time
|
||||
if (!isNote(value)) {
|
||||
@ -88,8 +178,6 @@ Pattern.prototype.autofilter = function (g: number) {
|
||||
return this.chain(autofilter(g));
|
||||
};
|
||||
|
||||
Pattern.prototype.patternified = Pattern.prototype.patternified.concat(['synth', 'gain', 'filter']);
|
||||
|
||||
Pattern.prototype.define('synth', (type, pat) => pat.synth(type), { composable: true, patternified: true });
|
||||
Pattern.prototype.define('gain', (gain, pat) => pat.synth(gain), { composable: true, patternified: true });
|
||||
Pattern.prototype.define('filter', (cutoff, pat) => pat.filter(cutoff), { composable: true, patternified: true });
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user