Skip to content

Commit

Permalink
Merge branch 'tidalcycles:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
edcrub authored Dec 25, 2024
2 parents 3007d8e + 26cc7e2 commit fb1dde3
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 3 deletions.
7 changes: 7 additions & 0 deletions packages/codemirror/widget.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,10 @@ registerWidget('_pitchwheel', (id, options = {}, pat) => {
const ctx = getCanvasWidget(id, options).getContext('2d');
return pat.pitchwheel({ ...options, ctx, id });
});

registerWidget('_spectrum', (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.spectrum({ ...options, ctx, id });
});
2 changes: 1 addition & 1 deletion packages/core/repl.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export function repl({
// set pattern methods that use this repl via closure
const injectPatternMethods = () => {
Pattern.prototype.p = function (id) {
if (id.startsWith('_') || id.endsWith('_')) {
if (typeof id === 'string' && (id.startsWith('_') || id.endsWith('_'))) {
// allows muting a pattern x with x_ or _x
return silence;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/draw/draw.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const getDrawContext = (id = 'test-canvas', options) => {
}, 200);
});
}
return canvas.getContext(contextType);
return canvas.getContext(contextType, { willReadFrequently: true });
};

let animationFrames = {};
Expand Down
3 changes: 2 additions & 1 deletion packages/superdough/superdough.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,12 @@ function getReverb(orbit, duration, fade, lp, dim, ir) {
export let analysers = {},
analysersData = {};

export function getAnalyserById(id, fftSize = 1024) {
export function getAnalyserById(id, fftSize = 1024, smoothingTimeConstant = 0.5) {
if (!analysers[id]) {
// make sure this doesn't happen too often as it piles up garbage
const analyserNode = getAudioContext().createAnalyser();
analyserNode.fftSize = fftSize;
analyserNode.smoothingTimeConstant = smoothingTimeConstant;
// getDestination().connect(analyserNode);
analysers[id] = analyserNode;
analysersData[id] = new Float32Array(analysers[id].frequencyBinCount);
Expand Down
1 change: 1 addition & 0 deletions packages/webaudio/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ This program is free software: you can redistribute it and/or modify it under th

export * from './webaudio.mjs';
export * from './scope.mjs';
export * from './spectrum.mjs';
export * from 'superdough';
69 changes: 69 additions & 0 deletions packages/webaudio/spectrum.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Pattern, clamp } from '@strudel/core';
import { getDrawContext, getTheme } from '@strudel/draw';
import { analysers, getAnalyzerData } from 'superdough';

/**
* Renders a spectrum analyzer for the incoming audio signal.
* @name spectrum
* @param {object} config optional config with options:
* @param {integer} thickness line thickness in px (default 3)
* @param {integer} speed scroll speed (default 1)
* @param {integer} min min db (default -80)
* @param {integer} max max db (default 0)
* @example
* n("<0 4 <2 3> 1>*3")
* .off(1/8, add(n(5)))
* .off(1/5, add(n(7)))
* .scale("d3:minor:pentatonic")
* .s('sine')
* .dec(.3).room(.5)
* ._spectrum()
*/
let latestColor = {};
Pattern.prototype.spectrum = function (config = {}) {
let id = config.id ?? 1;
return this.analyze(id).draw(
(haps) => {
config.color = haps[0]?.value?.color || latestColor[id] || getTheme().foreground;
latestColor[id] = config.color;
drawSpectrum(analysers[id], config);
},
{ id },
);
};

Pattern.prototype.scope = Pattern.prototype.tscope;

const lastFrames = new Map();

function drawSpectrum(
analyser,
{ thickness = 3, speed = 1, min = -80, max = 0, ctx = getDrawContext(), id = 1, color } = {},
) {
ctx.lineWidth = thickness;
ctx.strokeStyle = color;

if (!analyser) {
// if analyser is undefined, draw straight line
// it may be undefined when no sound has been played yet
return;
}
const scrollSize = speed;
const dataArray = getAnalyzerData('frequency', id);
const canvas = ctx.canvas;
ctx.fillStyle = color;
const bufferSize = analyser.frequencyBinCount;
let imageData = lastFrames.get(id) || ctx.getImageData(0, 0, canvas.width, canvas.height);
lastFrames.set(id, imageData);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.putImageData(imageData, -scrollSize, 0);
let q = canvas.width - speed;
for (let i = 0; i < bufferSize; i++) {
const normalized = clamp((dataArray[i] - min) / (max - min), 0, 1);
ctx.globalAlpha = normalized;
const next = (Math.log(i + 1) / Math.log(bufferSize)) * canvas.height;
const size = 2; //next - pos;
ctx.fillRect(q, canvas.height - next, scrollSize, size);
}
lastFrames.set(id, ctx.getImageData(0, 0, canvas.width, canvas.height));
}
69 changes: 69 additions & 0 deletions test/__snapshots__/examples.test.mjs.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7508,6 +7508,75 @@ exports[`runs examples > example "sometimesBy" example index 0 1`] = `
]
`;

exports[`runs examples > example "spectrum" example index 0 1`] = `
[
"[ -5/24 ⇜ (0/1 → 1/8) | note:F4 s:sine decay:0.3 room:0.5 ]",
"[ -2/15 ⇜ (0/1 → 1/5) | note:A4 s:sine decay:0.3 room:0.5 ]",
"[ -1/120 ⇜ (0/1 → 1/5) ⇝ 13/40 | note:A5 s:sine decay:0.3 room:0.5 ]",
"[ 0/1 → 1/3 | note:D3 s:sine decay:0.3 room:0.5 ]",
"[ 1/8 → 11/24 | note:D4 s:sine decay:0.3 room:0.5 ]",
"[ -1/120 ⇜ (1/5 → 13/40) | note:A5 s:sine decay:0.3 room:0.5 ]",
"[ 1/5 → 8/15 | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ 13/40 → 79/120 | note:G5 s:sine decay:0.3 room:0.5 ]",
"[ 1/3 → 2/3 | note:C4 s:sine decay:0.3 room:0.5 ]",
"[ 11/24 → 19/24 | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ 8/15 → 13/15 | note:F5 s:sine decay:0.3 room:0.5 ]",
"[ 79/120 → 119/120 | note:F6 s:sine decay:0.3 room:0.5 ]",
"[ 2/3 → 1/1 | note:G3 s:sine decay:0.3 room:0.5 ]",
"[ (19/24 → 1/1) ⇝ 9/8 | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ (13/15 → 1/1) ⇝ 6/5 | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ (119/120 → 1/1) ⇝ 53/40 | note:C6 s:sine decay:0.3 room:0.5 ]",
"[ 19/24 ⇜ (1/1 → 9/8) | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ 13/15 ⇜ (1/1 → 6/5) | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ 119/120 ⇜ (1/1 → 6/5) ⇝ 53/40 | note:C6 s:sine decay:0.3 room:0.5 ]",
"[ 1/1 → 4/3 | note:F3 s:sine decay:0.3 room:0.5 ]",
"[ 9/8 → 35/24 | note:F4 s:sine decay:0.3 room:0.5 ]",
"[ 119/120 ⇜ (6/5 → 53/40) | note:C6 s:sine decay:0.3 room:0.5 ]",
"[ 6/5 → 23/15 | note:A4 s:sine decay:0.3 room:0.5 ]",
"[ 53/40 → 199/120 | note:A5 s:sine decay:0.3 room:0.5 ]",
"[ 4/3 → 5/3 | note:D3 s:sine decay:0.3 room:0.5 ]",
"[ 35/24 → 43/24 | note:D4 s:sine decay:0.3 room:0.5 ]",
"[ 23/15 → 28/15 | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ 199/120 → 239/120 | note:G5 s:sine decay:0.3 room:0.5 ]",
"[ 5/3 → 2/1 | note:C4 s:sine decay:0.3 room:0.5 ]",
"[ (43/24 → 2/1) ⇝ 17/8 | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ (28/15 → 2/1) ⇝ 11/5 | note:F5 s:sine decay:0.3 room:0.5 ]",
"[ (239/120 → 2/1) ⇝ 93/40 | note:F6 s:sine decay:0.3 room:0.5 ]",
"[ 43/24 ⇜ (2/1 → 17/8) | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ 28/15 ⇜ (2/1 → 11/5) | note:F5 s:sine decay:0.3 room:0.5 ]",
"[ 239/120 ⇜ (2/1 → 11/5) ⇝ 93/40 | note:F6 s:sine decay:0.3 room:0.5 ]",
"[ 2/1 → 7/3 | note:A3 s:sine decay:0.3 room:0.5 ]",
"[ 17/8 → 59/24 | note:A4 s:sine decay:0.3 room:0.5 ]",
"[ 239/120 ⇜ (11/5 → 93/40) | note:F6 s:sine decay:0.3 room:0.5 ]",
"[ 11/5 → 38/15 | note:D5 s:sine decay:0.3 room:0.5 ]",
"[ 93/40 → 319/120 | note:D6 s:sine decay:0.3 room:0.5 ]",
"[ 7/3 → 8/3 | note:F3 s:sine decay:0.3 room:0.5 ]",
"[ 59/24 → 67/24 | note:F4 s:sine decay:0.3 room:0.5 ]",
"[ 38/15 → 43/15 | note:A4 s:sine decay:0.3 room:0.5 ]",
"[ 319/120 → 359/120 | note:A5 s:sine decay:0.3 room:0.5 ]",
"[ 8/3 → 3/1 | note:D3 s:sine decay:0.3 room:0.5 ]",
"[ (67/24 → 3/1) ⇝ 25/8 | note:D4 s:sine decay:0.3 room:0.5 ]",
"[ (43/15 → 3/1) ⇝ 16/5 | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ (359/120 → 3/1) ⇝ 133/40 | note:G5 s:sine decay:0.3 room:0.5 ]",
"[ 67/24 ⇜ (3/1 → 25/8) | note:D4 s:sine decay:0.3 room:0.5 ]",
"[ 43/15 ⇜ (3/1 → 16/5) | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ 359/120 ⇜ (3/1 → 16/5) ⇝ 133/40 | note:G5 s:sine decay:0.3 room:0.5 ]",
"[ 3/1 → 10/3 | note:C4 s:sine decay:0.3 room:0.5 ]",
"[ 25/8 → 83/24 | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ 359/120 ⇜ (16/5 → 133/40) | note:G5 s:sine decay:0.3 room:0.5 ]",
"[ 16/5 → 53/15 | note:F5 s:sine decay:0.3 room:0.5 ]",
"[ 133/40 → 439/120 | note:F6 s:sine decay:0.3 room:0.5 ]",
"[ 10/3 → 11/3 | note:G3 s:sine decay:0.3 room:0.5 ]",
"[ 83/24 → 91/24 | note:G4 s:sine decay:0.3 room:0.5 ]",
"[ 53/15 → 58/15 | note:C5 s:sine decay:0.3 room:0.5 ]",
"[ 439/120 → 479/120 | note:C6 s:sine decay:0.3 room:0.5 ]",
"[ 11/3 → 4/1 | note:F3 s:sine decay:0.3 room:0.5 ]",
"[ (91/24 → 4/1) ⇝ 33/8 | note:F4 s:sine decay:0.3 room:0.5 ]",
"[ (58/15 → 4/1) ⇝ 21/5 | note:A4 s:sine decay:0.3 room:0.5 ]",
"[ (479/120 → 4/1) ⇝ 173/40 | note:A5 s:sine decay:0.3 room:0.5 ]",
]
`;

exports[`runs examples > example "speed" example index 0 1`] = `
[
"[ 0/1 → 1/6 | s:bd speed:1 ]",
Expand Down
3 changes: 3 additions & 0 deletions test/runtime.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ strudel.Pattern.prototype._pitchwheel = function () {
strudel.Pattern.prototype._pianoroll = function () {
return this;
};
strudel.Pattern.prototype._spectrum = function () {
return this;
};
strudel.Pattern.prototype.markcss = function () {
return this;
};
Expand Down
4 changes: 4 additions & 0 deletions website/src/pages/learn/visual-feedback.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ What follows is the API doc of all the options you can pass:

<JsDoc client:idle name="pitchwheel" h={0} />

## Spectrum

<JsDoc client:idle name="spectrum" h={0} />

## markcss

<JsDoc client:idle name="markcss" h={0} />

0 comments on commit fb1dde3

Please sign in to comment.