mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-25 04:28:30 +00:00
commit
506989b0d7
@ -126,3 +126,10 @@ registerWidget('_scope', (id, options = {}, pat) => {
|
|||||||
const ctx = getCanvasWidget(id, options).getContext('2d');
|
const ctx = getCanvasWidget(id, options).getContext('2d');
|
||||||
return pat.tag(id).scope({ ...options, ctx, id });
|
return pat.tag(id).scope({ ...options, ctx, id });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
registerWidget('_pitchwheel', (id, options = {}, pat) => {
|
||||||
|
let _size = options.size || 200;
|
||||||
|
options = { width: _size, height: _size, ...options, size: _size / 5 };
|
||||||
|
const ctx = getCanvasWidget(id, options).getContext('2d');
|
||||||
|
return pat.pitchwheel({ ...options, ctx, id });
|
||||||
|
});
|
||||||
|
|||||||
@ -156,6 +156,7 @@ export function repl({
|
|||||||
return pattern;
|
return pattern;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger(`[eval] error: ${err.message}`, 'error');
|
logger(`[eval] error: ${err.message}`, 'error');
|
||||||
|
console.error(err);
|
||||||
updateState({ evalError: err, pending: false });
|
updateState({ evalError: err, pending: false });
|
||||||
onEvalError?.(err);
|
onEvalError?.(err);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,3 +3,4 @@ export * from './color.mjs';
|
|||||||
export * from './draw.mjs';
|
export * from './draw.mjs';
|
||||||
export * from './pianoroll.mjs';
|
export * from './pianoroll.mjs';
|
||||||
export * from './spiral.mjs';
|
export * from './spiral.mjs';
|
||||||
|
export * from './pitchwheel.mjs';
|
||||||
|
|||||||
127
packages/draw/pitchwheel.mjs
Normal file
127
packages/draw/pitchwheel.mjs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { Pattern, midiToFreq, getFrequency } from '@strudel/core';
|
||||||
|
import { getTheme, getDrawContext } from './draw.mjs';
|
||||||
|
|
||||||
|
const c = midiToFreq(36);
|
||||||
|
|
||||||
|
const circlePos = (cx, cy, radius, angle) => {
|
||||||
|
angle = angle * Math.PI * 2;
|
||||||
|
const x = Math.sin(angle) * radius + cx;
|
||||||
|
const y = Math.cos(angle) * radius + cy;
|
||||||
|
return [x, y];
|
||||||
|
};
|
||||||
|
|
||||||
|
const freq2angle = (freq, root) => {
|
||||||
|
return 0.5 - (Math.log2(freq / root) % 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function pitchwheel({
|
||||||
|
haps,
|
||||||
|
ctx,
|
||||||
|
id,
|
||||||
|
hapcircles = 1,
|
||||||
|
circle = 0,
|
||||||
|
edo = 12,
|
||||||
|
root = c,
|
||||||
|
thickness = 3,
|
||||||
|
hapRadius = 6,
|
||||||
|
mode = 'flake',
|
||||||
|
margin = 10,
|
||||||
|
} = {}) {
|
||||||
|
const connectdots = mode === 'polygon';
|
||||||
|
const centerlines = mode === 'flake';
|
||||||
|
const w = ctx.canvas.width;
|
||||||
|
const h = ctx.canvas.height;
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
const color = getTheme().foreground;
|
||||||
|
|
||||||
|
const size = Math.min(w, h);
|
||||||
|
const radius = size / 2 - thickness / 2 - hapRadius - margin;
|
||||||
|
const centerX = w / 2;
|
||||||
|
const centerY = h / 2;
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
haps = haps.filter((hap) => hap.hasTag(id));
|
||||||
|
}
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
ctx.lineWidth = thickness;
|
||||||
|
|
||||||
|
if (circle) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (edo) {
|
||||||
|
Array.from({ length: edo }, (_, i) => {
|
||||||
|
const angle = freq2angle(root * Math.pow(2, i / edo), root);
|
||||||
|
const [x, y] = circlePos(centerX, centerY, radius, angle);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, hapRadius, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
let shape = [];
|
||||||
|
ctx.lineWidth = hapRadius;
|
||||||
|
haps.forEach((hap) => {
|
||||||
|
let freq;
|
||||||
|
try {
|
||||||
|
freq = getFrequency(hap);
|
||||||
|
} catch (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const angle = freq2angle(freq, root);
|
||||||
|
const [x, y] = circlePos(centerX, centerY, radius, angle);
|
||||||
|
const hapColor = hap.value.color || color;
|
||||||
|
ctx.strokeStyle = hapColor;
|
||||||
|
ctx.fillStyle = hapColor;
|
||||||
|
const { velocity = 1, gain = 1 } = hap.value || {};
|
||||||
|
const alpha = velocity * gain;
|
||||||
|
ctx.globalAlpha = alpha;
|
||||||
|
shape.push([x, y, angle, hapColor, alpha]);
|
||||||
|
ctx.beginPath();
|
||||||
|
if (hapcircles) {
|
||||||
|
ctx.moveTo(x + hapRadius, y);
|
||||||
|
ctx.arc(x, y, hapRadius, 0, 2 * Math.PI);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
if (centerlines) {
|
||||||
|
ctx.moveTo(centerX, centerY);
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.globalAlpha = 1;
|
||||||
|
if (connectdots && shape.length) {
|
||||||
|
shape = shape.sort((a, b) => a[2] - b[2]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(shape[0][0], shape[0][1]);
|
||||||
|
shape.forEach(([x, y, _, color, alpha]) => {
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.globalAlpha = alpha;
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
});
|
||||||
|
ctx.lineTo(shape[0][0], shape[0][1]);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern.prototype.pitchwheel = function (options = {}) {
|
||||||
|
let { ctx = getDrawContext(), id = 1 } = options;
|
||||||
|
return this.tag(id).onPaint((_, time, haps) =>
|
||||||
|
pitchwheel({
|
||||||
|
...options,
|
||||||
|
time,
|
||||||
|
ctx,
|
||||||
|
haps: haps.filter((hap) => hap.isActive(time)),
|
||||||
|
id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -235,6 +235,11 @@ Object.keys(simple).forEach((symbol) => {
|
|||||||
let alias = symbol.replace('^', 'M');
|
let alias = symbol.replace('^', 'M');
|
||||||
voicingAlias(symbol, alias, [complex, simple]);
|
voicingAlias(symbol, alias, [complex, simple]);
|
||||||
}
|
}
|
||||||
|
// add aliases for "+" === "aug"
|
||||||
|
if (symbol.includes('+')) {
|
||||||
|
let alias = symbol.replace('+', 'aug');
|
||||||
|
voicingAlias(symbol, alias, [complex, simple]);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
registerVoicings('ireal', simple);
|
registerVoicings('ireal', simple);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user