add repl-react + fix deps
This commit is contained in:
parent
6f60a3a1d5
commit
6430f6207d
53 changed files with 33850 additions and 24809 deletions
53
packages/tone/draw.mjs
Normal file
53
packages/tone/draw.mjs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import * as Tone from 'tone';
|
||||
import { Pattern } from '@strudel/core';
|
||||
|
||||
export const getDrawContext = (id = 'test-canvas') => {
|
||||
let canvas = document.querySelector('#' + id);
|
||||
if (!canvas) {
|
||||
canvas = document.createElement('canvas');
|
||||
canvas.id = id;
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
canvas.style = 'pointer-events:none;width:100%;height:100%;position:fixed;top:0;left:0;z-index:5';
|
||||
document.body.prepend(canvas);
|
||||
}
|
||||
return canvas.getContext('2d');
|
||||
};
|
||||
|
||||
Pattern.prototype.draw = function (callback, cycleSpan, lookaheadCycles = 1) {
|
||||
if (window.strudelAnimation) {
|
||||
cancelAnimationFrame(window.strudelAnimation);
|
||||
}
|
||||
const ctx = getDrawContext();
|
||||
let cycle,
|
||||
events = [];
|
||||
const animate = (time) => {
|
||||
const t = Tone.getTransport().seconds;
|
||||
if (cycleSpan) {
|
||||
const currentCycle = Math.floor(t / cycleSpan);
|
||||
if (cycle !== currentCycle) {
|
||||
cycle = currentCycle;
|
||||
const begin = currentCycle * cycleSpan;
|
||||
const end = (currentCycle + lookaheadCycles) * cycleSpan;
|
||||
events = this._asNumber(true) // true = silent error
|
||||
.query(new State(new TimeSpan(begin, end)))
|
||||
.filter((event) => event.part.begin.equals(event.whole.begin));
|
||||
}
|
||||
}
|
||||
callback(ctx, events, t, cycleSpan, time);
|
||||
window.strudelAnimation = requestAnimationFrame(animate);
|
||||
};
|
||||
requestAnimationFrame(animate);
|
||||
return this;
|
||||
};
|
||||
|
||||
export const cleanup = () => {
|
||||
const ctx = getDrawContext();
|
||||
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
|
||||
if (window.strudelAnimation) {
|
||||
cancelAnimationFrame(window.strudelAnimation);
|
||||
}
|
||||
if (window.strudelScheduler) {
|
||||
clearInterval(window.strudelScheduler);
|
||||
}
|
||||
};
|
||||
38
packages/tone/pianoroll.mjs
Normal file
38
packages/tone/pianoroll.mjs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { Pattern } from '@strudel/core';
|
||||
|
||||
Pattern.prototype.pianoroll = function ({
|
||||
timeframe = 10,
|
||||
inactive = '#C9E597',
|
||||
active = '#FFCA28',
|
||||
background = '#2A3236',
|
||||
maxMidi = 90,
|
||||
minMidi = 0,
|
||||
} = {}) {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
const midiRange = maxMidi - minMidi + 1;
|
||||
const height = h / midiRange;
|
||||
this.draw(
|
||||
(ctx, events, t) => {
|
||||
ctx.fillStyle = background;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.fillRect(0, 0, w, h);
|
||||
events.forEach((event) => {
|
||||
const isActive = event.whole.begin <= t && event.whole.end >= t;
|
||||
ctx.fillStyle = event.context?.color || inactive;
|
||||
ctx.strokeStyle = event.context?.color || active;
|
||||
ctx.globalAlpha = event.context.velocity ?? 1;
|
||||
const x = Math.round((event.whole.begin / timeframe) * w);
|
||||
const width = Math.round(((event.whole.end - event.whole.begin) / timeframe) * w);
|
||||
const y = Math.round(h - ((Number(event.value) - minMidi) / midiRange) * h);
|
||||
const offset = (t / timeframe) * w;
|
||||
const margin = 0;
|
||||
const coords = [x - offset + margin + 1, y + 1, width - 2, height - 2];
|
||||
isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords);
|
||||
});
|
||||
},
|
||||
timeframe,
|
||||
2, // lookaheadCycles
|
||||
);
|
||||
return this;
|
||||
};
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { Pattern as _Pattern } from '../core/strudel.mjs';
|
||||
import Tone from 'tone';
|
||||
import { Pattern as _Pattern } from '@strudel/core';
|
||||
import * as Tone from 'tone';
|
||||
|
||||
const {
|
||||
AutoFilter,
|
||||
Destination,
|
||||
|
|
@ -20,8 +21,8 @@ const {
|
|||
getDestination,
|
||||
Players,
|
||||
} = Tone;
|
||||
/* import tonePiano from '@tonejs/piano';
|
||||
const { Piano } = tonePiano; */
|
||||
import * as tonePiano from '@tonejs/piano';
|
||||
const { Piano } = tonePiano;
|
||||
import { getPlayableNoteValue } from '../../packages/core/util.mjs';
|
||||
|
||||
// "balanced" | "interactive" | "playback";
|
||||
|
|
@ -106,11 +107,11 @@ export const players = (options, baseUrl = '') => {
|
|||
});
|
||||
};
|
||||
export const synth = (options) => new Synth(options);
|
||||
/* export const piano = async (options = { velocities: 1 }) => {
|
||||
export const piano = async (options = { velocities: 1 }) => {
|
||||
const p = new Piano(options);
|
||||
await p.load();
|
||||
return p;
|
||||
}; */
|
||||
};
|
||||
|
||||
// effect helpers
|
||||
export const vol = (v) => new Gain(v);
|
||||
|
|
|
|||
50
packages/tone/ui.mjs
Normal file
50
packages/tone/ui.mjs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import * as Tone from 'tone';
|
||||
|
||||
export const hideHeader = () => {
|
||||
document.getElementById('header').style = 'display:none';
|
||||
};
|
||||
|
||||
function frame(callback) {
|
||||
if (window.strudelAnimation) {
|
||||
cancelAnimationFrame(window.strudelAnimation);
|
||||
}
|
||||
const animate = (animationTime) => {
|
||||
const toneTime = Tone.getTransport().seconds;
|
||||
callback(animationTime, toneTime);
|
||||
window.strudelAnimation = requestAnimationFrame(animate);
|
||||
};
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
export const backgroundImage = function (src, animateOptions = {}) {
|
||||
const container = document.getElementById('code');
|
||||
const bg = 'background-image:url(' + src + ');background-size:contain;';
|
||||
container.style = bg;
|
||||
const { className: initialClassName } = container;
|
||||
const handleOption = (option, value) => {
|
||||
({
|
||||
style: () => (container.style = bg + ';' + value),
|
||||
className: () => (container.className = value + ' ' + initialClassName),
|
||||
}[option]());
|
||||
};
|
||||
const funcOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'function');
|
||||
const stringOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'string');
|
||||
stringOptions.forEach(([option, value]) => handleOption(option, value));
|
||||
|
||||
if (funcOptions.length === 0) {
|
||||
return;
|
||||
}
|
||||
frame((_, t) =>
|
||||
funcOptions.forEach(([option, value]) => {
|
||||
handleOption(option, value(t));
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const cleanup = () => {
|
||||
const container = document.getElementById('code');
|
||||
if (container) {
|
||||
container.style = '';
|
||||
container.className = 'grow relative'; // has to match App.tsx
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue