mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 13:48:40 +00:00
76 lines
3.3 KiB
JavaScript
76 lines
3.3 KiB
JavaScript
/*
|
|
static.mjs - <short description TODO>
|
|
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/static.mjs>
|
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { Tone } from '@strudel.cycles/tone';
|
|
import { State, TimeSpan } from '@strudel.cycles/core';
|
|
import { getPlayableNoteValue } from '@strudel.cycles/core/util.mjs';
|
|
import { evaluate } from '@strudel.cycles/eval';
|
|
import { getDefaultSynth } from '@strudel.cycles/tone';
|
|
|
|
const defaultSynth = getDefaultSynth();
|
|
|
|
// this is a test to play back events with as less runtime code as possible..
|
|
// the code asks for the number of seconds to prequery
|
|
// after the querying is done, the events are scheduled
|
|
// after the scheduling is done, the transport is started
|
|
// nothing happens while tone.js runs except the schedule callback, which is a thin wrapper around triggerAttackRelease calls
|
|
// so all glitches that appear here should have nothing to do with strudel and or the repl
|
|
|
|
async function playStatic(code) {
|
|
Tone.getTransport().cancel();
|
|
Tone.getTransport().stop();
|
|
let start, took;
|
|
const seconds = Number(prompt('How many seconds to run?')) || 60;
|
|
start = performance.now();
|
|
console.log('evaluating..');
|
|
const { pattern: pat } = await evaluate(code);
|
|
took = performance.now() - start;
|
|
console.log('evaluate took', took, 'ms');
|
|
console.log('querying..');
|
|
start = performance.now();
|
|
const events = pat
|
|
?.query(new State(new TimeSpan(0, seconds)))
|
|
?.filter((event) => event.part.begin.equals(event.whole.begin))
|
|
?.map((event) => ({
|
|
time: event.whole.begin.valueOf(),
|
|
duration: event.whole.end.sub(event.whole.begin).valueOf(),
|
|
value: event.value,
|
|
context: event.context,
|
|
}));
|
|
took = performance.now() - start;
|
|
console.log('query took', took, 'ms');
|
|
console.log('scheduling..');
|
|
start = performance.now();
|
|
events.forEach((event) => {
|
|
Tone.getTransport().schedule((time) => {
|
|
try {
|
|
const { onTrigger, velocity } = event.context;
|
|
if (!onTrigger) {
|
|
if (defaultSynth) {
|
|
const note = getPlayableNoteValue(event);
|
|
defaultSynth.triggerAttackRelease(note, event.duration.valueOf(), time, velocity);
|
|
} else {
|
|
throw new Error('no defaultSynth passed to useRepl.');
|
|
}
|
|
} else {
|
|
onTrigger(time, event);
|
|
}
|
|
} catch (err) {
|
|
console.warn(err);
|
|
err.message = 'unplayable event: ' + err?.message;
|
|
console.error(err);
|
|
}
|
|
}, event.time);
|
|
});
|
|
took = performance.now() - start;
|
|
console.log('scheduling took', took, 'ms');
|
|
console.log('now starting!');
|
|
|
|
Tone.getTransport().start('+0.5');
|
|
}
|
|
|
|
export default playStatic;
|