diff --git a/repl/src/draw.mjs b/repl/src/draw.mjs index 2a3e85fc..aa8a6d2a 100644 --- a/repl/src/draw.mjs +++ b/repl/src/draw.mjs @@ -1,4 +1,5 @@ import * as Tone from 'tone'; +import { Pattern } from '../../strudel.mjs'; export const getDrawContext = (id = 'test-canvas') => { let canvas = document.querySelector('#' + id); @@ -13,35 +14,27 @@ export const getDrawContext = (id = 'test-canvas') => { return canvas.getContext('2d'); }; -export const draw = (callback) => { +Pattern.prototype.draw = function (callback, duration) { if (window.strudelAnimation) { cancelAnimationFrame(window.strudelAnimation); } - const animate = (t) => { - callback(t); + const ctx = getDrawContext(); + let cycle, events; + const animate = (time) => { + const t = Tone.getTransport().seconds; + const currentCycle = Math.floor(t / duration); + if (cycle !== currentCycle) { + cycle = currentCycle; + const begin = currentCycle * duration; + const end = (currentCycle + 2) * duration; + events = this.add(0).query(new State(new TimeSpan(begin, end))); + } + callback(ctx, events, t, time); window.strudelAnimation = requestAnimationFrame(animate); }; requestAnimationFrame(animate); }; -export const queryEvents = (pattern, callback, seconds) => { - const queryEvents = () => { - const t = Tone.getTransport().seconds; - const begin = Math.floor(t / seconds) * seconds; - const end = begin + seconds * 4; - // console.log('query', t, begin, end); - const events = pattern.add(0).query(new State(new TimeSpan(begin, end))); - callback(events); - }; - queryEvents(); - if (window.strudelScheduler) { - clearInterval(window.strudelScheduler); - } - window.strudelScheduler = setInterval(() => { - queryEvents(); - }, seconds * 1.5 * 1000); -}; - export const cleanup = () => { const ctx = getDrawContext(); ctx.clearRect(0, 0, window.innerWidth, window.innerHeight); diff --git a/repl/src/evaluate.ts b/repl/src/evaluate.ts index a38937e8..546433c1 100644 --- a/repl/src/evaluate.ts +++ b/repl/src/evaluate.ts @@ -7,6 +7,7 @@ import './xen.mjs'; import './tune.mjs'; import './tune.mjs'; import './pianoroll.mjs'; +import './draw.mjs'; import * as drawHelpers from './draw.mjs'; import gist from './gist.js'; import shapeshifter from './shapeshifter'; diff --git a/repl/src/pianoroll.mjs b/repl/src/pianoroll.mjs index cac1bcbd..634f3345 100644 --- a/repl/src/pianoroll.mjs +++ b/repl/src/pianoroll.mjs @@ -1,41 +1,30 @@ -import { draw, queryEvents, getDrawContext } from './draw.mjs'; import { Pattern } from '../../strudel.mjs'; -import * as Tone from 'tone'; -Pattern.prototype.pianoroll = function () { - // draw stuff here with p.query - const ctx = getDrawContext(); +Pattern.prototype.pianoroll = function ({ + timeframe = 10, + inactive = '#88ABF8', + active = '#FFCA28', + background = '#2A3236', + maxMidi = 90, + minMidi = 0, +} = {}) { const w = window.innerWidth; const h = window.innerHeight; - const s = 10; // 10s in viewport - const maxMidi = 90; - const height = h / maxMidi; - let events; - - queryEvents(this, (_events) => (events = _events), s); - - const clear = () => ctx.clearRect(0, 0, w, h); - - const drawEvents = (events) => { - events.forEach((event) => { - const t = Tone.getTransport().seconds; - const isActive = event.whole.begin <= t && event.whole.end >= t; - ctx.fillStyle = isActive ? '#FFCA28' : '#88ABF8'; - const x = Math.round((event.whole.begin / s) * w); - const width = Math.round(((event.whole.end - event.whole.begin) / s) * w); - const y = Math.round(h - (Number(event.value) / maxMidi) * h); - const offset = (t / s) * w; - const margin = 0; - // console.log(x, y, width, height) - ctx.fillRect(x - offset + margin, y, width, height); - }); - }; - - draw((t) => { - clear(); - ctx.fillStyle = '#2A3236'; + const midiRange = maxMidi - minMidi + 1; + const height = h / midiRange; + this.draw((ctx, events, t) => { + ctx.fillStyle = background; ctx.fillRect(0, 0, w, h); - drawEvents(events); - }); + events.forEach((event) => { + const isActive = event.whole.begin <= t && event.whole.end >= t; + ctx.fillStyle = isActive ? active : inactive; + 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; + ctx.fillRect(x - offset + margin + 1, y + 1, width - 2, height - 2); + }); + }, timeframe); return this; };