From 010329dcfced36b4e394e69d999fb5a684e52619 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 1 Apr 2024 15:19:22 +0200 Subject: [PATCH 1/6] fix: proper error log with call stack --- packages/core/repl.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index e073dfe8..3a2e6bd6 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -156,6 +156,7 @@ export function repl({ return pattern; } catch (err) { logger(`[eval] error: ${err.message}`, 'error'); + console.error(err); updateState({ evalError: err, pending: false }); onEvalError?.(err); } From 8a3e9171a439664d1ba621eaca48bda376dffab3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 1 Apr 2024 15:19:50 +0200 Subject: [PATCH 2/6] basic pitchwheel vis --- packages/codemirror/widget.mjs | 7 ++ packages/draw/index.mjs | 1 + packages/draw/pitchwheel.mjs | 114 +++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 packages/draw/pitchwheel.mjs diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index 1d86dc35..ae62b842 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -126,3 +126,10 @@ registerWidget('_scope', (id, options = {}, pat) => { const ctx = getCanvasWidget(id, options).getContext('2d'); 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.tag(id).pitchwheel({ ...options, ctx, id }); +}); diff --git a/packages/draw/index.mjs b/packages/draw/index.mjs index 89cda805..506c6151 100644 --- a/packages/draw/index.mjs +++ b/packages/draw/index.mjs @@ -3,3 +3,4 @@ export * from './color.mjs'; export * from './draw.mjs'; export * from './pianoroll.mjs'; export * from './spiral.mjs'; +export * from './pitchwheel.mjs'; diff --git a/packages/draw/pitchwheel.mjs b/packages/draw/pitchwheel.mjs new file mode 100644 index 00000000..c0c79118 --- /dev/null +++ b/packages/draw/pitchwheel.mjs @@ -0,0 +1,114 @@ +import { Pattern, midiToFreq } 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({ + time, + haps, + ctx, + id, + connectdots = 0, + centerlines = 1, + circle = 0, + edo = 12, + root = c, +} = {}) { + const w = ctx.canvas.width; + const h = ctx.canvas.height; + ctx.clearRect(0, 0, w, h); + const color = getTheme().foreground; + const hapRadius = 10; + const margin = 10; + + const size = Math.min(w, h); + const thickness = 4; + 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) => { + ctx.beginPath(); + const angle = freq2angle(root * Math.pow(2, i / edo), root); + const [x, y] = circlePos(centerX, centerY, radius, angle); + ctx.arc(x, y, hapRadius, 0, 2 * Math.PI); + ctx.stroke(); + }); + } + + let shape = []; + haps.forEach((hap) => { + const freq = getFrequency(hap); + const angle = freq2angle(freq, root); + const [x, y] = circlePos(centerX, centerY, radius, angle); + const hapColor = hap.value.color || color; + shape.push([x, y]); + ctx.strokeStyle = hapColor; + ctx.fillStyle = hapColor; + const { velocity = 1, gain = 1 } = hap.value || {}; + ctx.globalAlpha = velocity * gain; + ctx.beginPath(); + 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 (shape.length && connectdots) { + ctx.beginPath(); + ctx.moveTo(shape[0][0], shape[0][1]); + shape.forEach(([x, y]) => { + 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; + this.onPaint((_, time, haps) => + pitchwheel({ + ...options, + time, + ctx, + haps: haps.filter((hap) => hap.isActive(time)), + id, + }), + ); + return this; +}; From 539160630dd5e7a486204998df6fbbcd08bf3749 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 1 Apr 2024 15:30:19 +0200 Subject: [PATCH 3/6] more settings --- packages/draw/pitchwheel.mjs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/draw/pitchwheel.mjs b/packages/draw/pitchwheel.mjs index c0c79118..ced0c579 100644 --- a/packages/draw/pitchwheel.mjs +++ b/packages/draw/pitchwheel.mjs @@ -1,4 +1,4 @@ -import { Pattern, midiToFreq } from '@strudel/core'; +import { Pattern, midiToFreq, getFrequency } from '@strudel/core'; import { getTheme, getDrawContext } from './draw.mjs'; const c = midiToFreq(36); @@ -21,19 +21,20 @@ export function pitchwheel({ id, connectdots = 0, centerlines = 1, + hapcircles = 1, circle = 0, edo = 12, root = c, + thickness = 4, + hapRadius = 4, + margin = 10, } = {}) { const w = ctx.canvas.width; const h = ctx.canvas.height; ctx.clearRect(0, 0, w, h); const color = getTheme().foreground; - const hapRadius = 10; - const margin = 10; const size = Math.min(w, h); - const thickness = 4; const radius = size / 2 - thickness / 2 - hapRadius - margin; const centerX = w / 2; const centerY = h / 2; @@ -74,9 +75,11 @@ export function pitchwheel({ const { velocity = 1, gain = 1 } = hap.value || {}; ctx.globalAlpha = velocity * gain; ctx.beginPath(); - ctx.moveTo(x + hapRadius, y); - ctx.arc(x, y, hapRadius, 0, 2 * Math.PI); - ctx.fill(); + 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); From c7f811e14d8c8cecc0004620e1846ac672736988 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 1 Apr 2024 17:04:03 +0200 Subject: [PATCH 4/6] fix: ignore haps that error at getFrequency --- packages/draw/pitchwheel.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/draw/pitchwheel.mjs b/packages/draw/pitchwheel.mjs index ced0c579..76978d08 100644 --- a/packages/draw/pitchwheel.mjs +++ b/packages/draw/pitchwheel.mjs @@ -65,7 +65,12 @@ export function pitchwheel({ let shape = []; haps.forEach((hap) => { - const freq = getFrequency(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; From 6479c603876fce72c51885a7069dacac3a1b62e0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 1 Apr 2024 23:21:32 +0200 Subject: [PATCH 5/6] tweaks --- packages/codemirror/widget.mjs | 2 +- packages/draw/pitchwheel.mjs | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/codemirror/widget.mjs b/packages/codemirror/widget.mjs index ae62b842..72b4ec65 100644 --- a/packages/codemirror/widget.mjs +++ b/packages/codemirror/widget.mjs @@ -131,5 +131,5 @@ 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.tag(id).pitchwheel({ ...options, ctx, id }); + return pat.pitchwheel({ ...options, ctx, id }); }); diff --git a/packages/draw/pitchwheel.mjs b/packages/draw/pitchwheel.mjs index 76978d08..8d3cfe61 100644 --- a/packages/draw/pitchwheel.mjs +++ b/packages/draw/pitchwheel.mjs @@ -15,20 +15,20 @@ const freq2angle = (freq, root) => { }; export function pitchwheel({ - time, haps, ctx, id, - connectdots = 0, - centerlines = 1, hapcircles = 1, circle = 0, edo = 12, root = c, - thickness = 4, - hapRadius = 4, + 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); @@ -55,15 +55,17 @@ export function pitchwheel({ if (edo) { Array.from({ length: edo }, (_, i) => { - ctx.beginPath(); 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.stroke(); + ctx.fill(); }); + ctx.stroke(); } let shape = []; + ctx.lineWidth = hapRadius; haps.forEach((hap) => { let freq; try { @@ -74,11 +76,12 @@ export function pitchwheel({ const angle = freq2angle(freq, root); const [x, y] = circlePos(centerX, centerY, radius, angle); const hapColor = hap.value.color || color; - shape.push([x, y]); ctx.strokeStyle = hapColor; ctx.fillStyle = hapColor; const { velocity = 1, gain = 1 } = hap.value || {}; - ctx.globalAlpha = velocity * gain; + const alpha = velocity * gain; + ctx.globalAlpha = alpha; + shape.push([x, y, angle, hapColor, alpha]); ctx.beginPath(); if (hapcircles) { ctx.moveTo(x + hapRadius, y); @@ -94,10 +97,13 @@ export function pitchwheel({ ctx.strokeStyle = color; ctx.globalAlpha = 1; - if (shape.length && connectdots) { + 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]) => { + shape.forEach(([x, y, _, color, alpha]) => { + ctx.strokeStyle = color; + ctx.globalAlpha = alpha; ctx.lineTo(x, y); }); ctx.lineTo(shape[0][0], shape[0][1]); @@ -109,7 +115,7 @@ export function pitchwheel({ Pattern.prototype.pitchwheel = function (options = {}) { let { ctx = getDrawContext(), id = 1 } = options; - this.onPaint((_, time, haps) => + return this.tag(id).onPaint((_, time, haps) => pitchwheel({ ...options, time, @@ -118,5 +124,4 @@ Pattern.prototype.pitchwheel = function (options = {}) { id, }), ); - return this; }; From e242e82b3fad0ac79484243aa85ebc512e3e8646 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 1 Apr 2024 23:21:55 +0200 Subject: [PATCH 6/6] ireal voicings add aug alias for + --- packages/tonal/voicings.mjs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/tonal/voicings.mjs b/packages/tonal/voicings.mjs index 66fcd03c..0a08575d 100644 --- a/packages/tonal/voicings.mjs +++ b/packages/tonal/voicings.mjs @@ -235,6 +235,11 @@ Object.keys(simple).forEach((symbol) => { let alias = symbol.replace('^', 'M'); 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);