Merge branch 'main' into main
This commit is contained in:
commit
57f2a97b7b
6 changed files with 50 additions and 21 deletions
|
|
@ -153,6 +153,7 @@ samples('github:tidalcycles/dirt-samples')
|
||||||
|
|
||||||
The format is `github:<user>/<repo>/<branch>`.
|
The format is `github:<user>/<repo>/<branch>`.
|
||||||
|
|
||||||
|
If `<repo>` and `<branch>` are not specified, they will default to `samples` and `main` respectively.
|
||||||
It expects a `strudel.json` file to be present at the root of the given repository, which declares the sample paths in the repo.
|
It expects a `strudel.json` file to be present at the root of the given repository, which declares the sample paths in the repo.
|
||||||
|
|
||||||
The format is also expected to be the same as explained above.
|
The format is also expected to be the same as explained above.
|
||||||
|
|
|
||||||
|
|
@ -121,13 +121,20 @@ function githubPath(base, subpath = '') {
|
||||||
if (!base.startsWith('github:')) {
|
if (!base.startsWith('github:')) {
|
||||||
throw new Error('expected "github:" at the start of pseudoUrl');
|
throw new Error('expected "github:" at the start of pseudoUrl');
|
||||||
}
|
}
|
||||||
let [_, path] = base.split('github:');
|
let path = base.slice('github:'.length);
|
||||||
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
path = path.endsWith('/') ? path.slice(0, -1) : path;
|
||||||
if (path.split('/').length === 2) {
|
|
||||||
// assume main as default branch if none set
|
let components = path.split('/');
|
||||||
path += '/main';
|
let user = components[0];
|
||||||
|
let repo = components.length >= 2 ? components[1] : 'samples';
|
||||||
|
let branch = components.length >= 3 ? components[2] : 'main';
|
||||||
|
let other = components.slice(3);
|
||||||
|
if (subpath) {
|
||||||
|
other.push(subpath);
|
||||||
}
|
}
|
||||||
return `https://raw.githubusercontent.com/${path}/${subpath}`;
|
other = other.join('/');
|
||||||
|
|
||||||
|
return `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${other}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
|
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
|
||||||
|
|
|
||||||
|
|
@ -231,12 +231,12 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
|
||||||
begin: t,
|
begin: t,
|
||||||
end: envEnd,
|
end: envEnd,
|
||||||
frequency,
|
frequency,
|
||||||
detune: value.detune,
|
freqspread: value.detune,
|
||||||
position: value.wt,
|
position: value.wt,
|
||||||
warp: value.warp,
|
warp: value.warp,
|
||||||
warpMode: warpmode,
|
warpMode: warpmode,
|
||||||
voices: Math.max(value.unison ?? 1, 1),
|
voices: Math.max(value.unison ?? 1, 1),
|
||||||
spread: value.spread,
|
panspread: value.spread,
|
||||||
phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0,
|
phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0,
|
||||||
},
|
},
|
||||||
{ outputChannelCount: [2] },
|
{ outputChannelCount: [2] },
|
||||||
|
|
|
||||||
|
|
@ -1050,14 +1050,15 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||||
return [
|
return [
|
||||||
{ name: 'begin', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
{ name: 'begin', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
||||||
{ name: 'end', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
{ name: 'end', defaultValue: 0, min: 0, max: Number.POSITIVE_INFINITY },
|
||||||
{ name: 'frequency', defaultValue: 220, minValue: 0.01, maxValue: 20000 },
|
{ name: 'frequency', defaultValue: 440, min: Number.EPSILON },
|
||||||
{ name: 'detune', defaultValue: 0.18 },
|
{ name: 'detune', defaultValue: 0 },
|
||||||
{ name: 'position', defaultValue: 0, minValue: 0, maxValue: 1 },
|
{ name: 'freqspread', defaultValue: 0.18, min: 0 },
|
||||||
{ name: 'warp', defaultValue: 0, minValue: 0, maxValue: 1 },
|
{ name: 'position', defaultValue: 0, min: 0, max: 1 },
|
||||||
|
{ name: 'warp', defaultValue: 0, min: 0, max: 1 },
|
||||||
{ name: 'warpMode', defaultValue: 0 },
|
{ name: 'warpMode', defaultValue: 0 },
|
||||||
{ name: 'voices', defaultValue: 1, minValue: 1, maxValue: 32 },
|
{ name: 'voices', defaultValue: 1, min: 1 },
|
||||||
{ name: 'spread', defaultValue: 0.7, minValue: 0, maxValue: 1 },
|
{ name: 'panspread', defaultValue: 0.7, min: 0, max: 1 },
|
||||||
{ name: 'phaserand', defaultValue: 0, minValue: 0, maxValue: 1 },
|
{ name: 'phaserand', defaultValue: 0, min: 0, max: 1 },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1235,10 +1236,12 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||||
_sampleFrame(frame, phase) {
|
_sampleFrame(frame, phase) {
|
||||||
const len = frame.length;
|
const len = frame.length;
|
||||||
const pos = phase * len;
|
const pos = phase * len;
|
||||||
const i = pos | 0;
|
let i = pos | 0;
|
||||||
|
if (i >= len) i = 0; // fast wrap
|
||||||
const frac = pos - i;
|
const frac = pos - i;
|
||||||
const a = frame[i];
|
const a = frame[i];
|
||||||
const i1 = i + 1 < len ? i + 1 : 0; // fast wrap
|
let i1 = i + 1;
|
||||||
|
if (i1 >= len) i1 = 0;
|
||||||
const b = frame[i1];
|
const b = frame[i1];
|
||||||
return a + (b - a) * frac;
|
return a + (b - a) * frac;
|
||||||
}
|
}
|
||||||
|
|
@ -1268,6 +1271,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||||
}
|
}
|
||||||
for (let i = 0; i < outL.length; i++) {
|
for (let i = 0; i < outL.length; i++) {
|
||||||
const detune = pv(parameters.detune, i);
|
const detune = pv(parameters.detune, i);
|
||||||
|
const freqspread = pv(parameters.freqspread, i);
|
||||||
const tablePos = clamp(pv(parameters.position, i), 0, 1);
|
const tablePos = clamp(pv(parameters.position, i), 0, 1);
|
||||||
const idx = tablePos * (this.numFrames - 1);
|
const idx = tablePos * (this.numFrames - 1);
|
||||||
const fIdx = idx | 0;
|
const fIdx = idx | 0;
|
||||||
|
|
@ -1276,9 +1280,9 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||||
const warpMode = pv(parameters.warpMode, i);
|
const warpMode = pv(parameters.warpMode, i);
|
||||||
const voices = pv(parameters.voices, i);
|
const voices = pv(parameters.voices, i);
|
||||||
const phaseRand = clamp(pv(parameters.phaserand, i), 0, 1);
|
const phaseRand = clamp(pv(parameters.phaserand, i), 0, 1);
|
||||||
const spread = voices > 1 ? clamp(pv(parameters.spread, i), 0, 1) : 0;
|
const panspread = voices > 1 ? clamp(pv(parameters.panspread, i), 0, 1) : 0;
|
||||||
const gain1 = Math.sqrt(0.5 - 0.5 * spread);
|
const gain1 = Math.sqrt(0.5 - 0.5 * panspread);
|
||||||
const gain2 = Math.sqrt(0.5 + 0.5 * spread);
|
const gain2 = Math.sqrt(0.5 + 0.5 * panspread);
|
||||||
let f = pv(parameters.frequency, i);
|
let f = pv(parameters.frequency, i);
|
||||||
f = applySemitoneDetuneToFrequency(f, detune / 100); // overall detune
|
f = applySemitoneDetuneToFrequency(f, detune / 100); // overall detune
|
||||||
const normalizer = 1 / Math.sqrt(voices);
|
const normalizer = 1 / Math.sqrt(voices);
|
||||||
|
|
@ -1291,7 +1295,7 @@ class WavetableOscillatorProcessor extends AudioWorkletProcessor {
|
||||||
gainL = gain2;
|
gainL = gain2;
|
||||||
gainR = gain1;
|
gainR = gain1;
|
||||||
}
|
}
|
||||||
const fVoice = applySemitoneDetuneToFrequency(f, getUnisonDetune(voices, detune, n)); // voice detune
|
const fVoice = applySemitoneDetuneToFrequency(f, getUnisonDetune(voices, freqspread, n)); // voice detune
|
||||||
const dPhase = fVoice * this.invSR;
|
const dPhase = fVoice * this.invSR;
|
||||||
const level = this._chooseMip(dPhase);
|
const level = this._chooseMip(dPhase);
|
||||||
const table = this.tables[level];
|
const table = this.tables[level];
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,23 @@ Strudel makes heavy use of chained functions. Here is a more sophisticated examp
|
||||||
.room(0.5)`}
|
.room(0.5)`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
## Write your own chained function
|
||||||
|
|
||||||
|
You can write your own chained function using `register`. Here's the above chain but registered as a reusable, chained function.
|
||||||
|
|
||||||
|
<MiniRepl
|
||||||
|
client:idle
|
||||||
|
tune={`const effectChain = register('effectChain', (pat) => pat
|
||||||
|
.s("sawtooth")
|
||||||
|
.cutoff(500)
|
||||||
|
//.delay(0.5)
|
||||||
|
.room(0.5)
|
||||||
|
)
|
||||||
|
note("a3 c#4 e4 a4").effectChain()`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
Try adding `.rev()` after `effectChain()` to hear further effects added.
|
||||||
|
|
||||||
# Comments
|
# Comments
|
||||||
|
|
||||||
The `//` in the example above is a line comment, resulting in the `delay` function being ignored.
|
The `//` in the example above is a line comment, resulting in the `delay` function being ignored.
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ Sampler effects are functions that can be used to change the behaviour of sample
|
||||||
|
|
||||||
### scrub
|
### scrub
|
||||||
|
|
||||||
<JsDoc client:idle name="Pattern.scrub" h={0} />{' '}
|
<JsDoc client:idle name="Pattern.scrub" h={0} />
|
||||||
|
|
||||||
### speed
|
### speed
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue