mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-15 23:33:54 -04:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd6a2c6650 | |||
| c5a2ae47d7 | |||
| 17654b885b | |||
| acaf0ca0af | |||
| 6a201be1fd | |||
| 8617b9164e | |||
| 991e5f1b3c | |||
| 55c533c947 | |||
| d17543d5d9 | |||
| 73169563c4 | |||
| d351cb9f7f | |||
| 251be60630 | |||
| 18d7222804 | |||
| ff1c37d361 |
+8
-1
@@ -123,7 +123,14 @@ To publish all packages that have been changed since the last release, run:
|
||||
|
||||
```sh
|
||||
npm login
|
||||
npx lerna publish
|
||||
|
||||
# this will increment all the versions in package.json files of non private packages to selected versions
|
||||
npx lerna version --no-private
|
||||
|
||||
# publish all packages inside /packages using pnpm! don't use lerna to publish!!
|
||||
pnpm --filter "./packages/**" publish --dry-run
|
||||
|
||||
# the last command was only a dry-run, make sure everything looks ok, if yes, run the same command without flag
|
||||
```
|
||||
|
||||
To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/core",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.2",
|
||||
"description": "Port of Tidal Cycles to JavaScript",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
+2
-13
@@ -41,24 +41,13 @@ export function repl({
|
||||
throw new Error('no code to evaluate');
|
||||
}
|
||||
try {
|
||||
beforeEval?.({ code });
|
||||
scheduler.setCps(1); // reset cps in case the code does not contain a setCps call
|
||||
// problem: when the code does contain a setCps after an awaited promise,
|
||||
// the cps will be 1 until the promise resolves
|
||||
// example:
|
||||
/*
|
||||
await new Promise(resolve => setTimeout(resolve,1000))
|
||||
setCps(.5)
|
||||
note("c a f e")
|
||||
*/
|
||||
// to make sure the setCps inside the code is called immediately,
|
||||
// it has to be placed first
|
||||
await beforeEval?.({ code });
|
||||
let { pattern } = await _evaluate(code, transpiler);
|
||||
|
||||
logger(`[eval] code updated`);
|
||||
pattern = editPattern?.(pattern) || pattern;
|
||||
await afterEval?.({ code, pattern });
|
||||
scheduler.setPattern(pattern, autostart);
|
||||
afterEval?.({ code, pattern });
|
||||
return pattern;
|
||||
} catch (err) {
|
||||
// console.warn(`[repl] eval error: ${err.message}`);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/csound",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "csound bindings for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/eval",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Code evaluator for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/midi",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Midi API for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/mini",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.2",
|
||||
"description": "Mini notation for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "OSC messaging for strudel",
|
||||
"main": "osc.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/react",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "React components for strudel",
|
||||
"main": "src/index.js",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -82,7 +82,7 @@ export function MiniRepl({
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.code === 'Period') {
|
||||
} else if (e.key === '.') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
@@ -93,26 +93,6 @@ export function MiniRepl({
|
||||
),
|
||||
);
|
||||
|
||||
// set active pattern on ctrl+enter
|
||||
useLayoutEffect(() => {
|
||||
if (enableKeyboard) {
|
||||
const handleKeyPress = async (e) => {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.code === 'Period') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyPress, true);
|
||||
return () => window.removeEventListener('keydown', handleKeyPress, true);
|
||||
}
|
||||
}, [enableKeyboard, pattern, code, evaluate, stop, view]);
|
||||
|
||||
const [log, setLog] = useState([]);
|
||||
useLogger(
|
||||
useCallback((e) => {
|
||||
|
||||
@@ -46,17 +46,17 @@ function useStrudel({
|
||||
drawContext,
|
||||
transpiler,
|
||||
editPattern,
|
||||
beforeEval: ({ code }) => {
|
||||
beforeEval: async ({ code }) => {
|
||||
setCode(code);
|
||||
beforeEval?.();
|
||||
await beforeEval?.();
|
||||
},
|
||||
afterEval: (res) => {
|
||||
afterEval: async (res) => {
|
||||
const { pattern: _pattern, code } = res;
|
||||
setActiveCode(code);
|
||||
setPattern(_pattern);
|
||||
setEvalError();
|
||||
setSchedulerError();
|
||||
afterEval?.(res);
|
||||
await afterEval?.(res);
|
||||
},
|
||||
onToggle: (v) => {
|
||||
setStarted(v);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/serial",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Webserial API for strudel",
|
||||
"main": "serial.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/soundfonts",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Soundsfont support for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tonal",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Tonal functions for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/tone",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Tone.js API for strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/transpiler",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webaudio",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Web Audio helpers for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -170,7 +170,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration, cps) => {
|
||||
sourceNode = source(t, hap.value, hapDuration);
|
||||
} else if (getSound(s)) {
|
||||
const { onTrigger } = getSound(s);
|
||||
const soundHandle = await onTrigger(t, hap.value, onended);
|
||||
const soundHandle = await onTrigger(t, { ...hap.value, s }, onended);
|
||||
if (soundHandle) {
|
||||
sourceNode = soundHandle.node;
|
||||
soundHandle.stop(t + hapDuration);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/webdirt",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "WebDirt integration for Strudel",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@strudel.cycles/xen",
|
||||
"version": "0.7.0",
|
||||
"version": "0.7.1",
|
||||
"description": "Xenharmonic API for strudel",
|
||||
"main": "index.mjs",
|
||||
"publishConfig": {
|
||||
|
||||
@@ -58,5 +58,6 @@ These packages provide bindings for different ways to output strudel patterns:
|
||||
|
||||
## Tools
|
||||
|
||||
- [pnpm workspaces](https://pnpm.io/)
|
||||
- Publishing packages is done with [lerna](https://lerna.js.org/).
|
||||
- [pnpm](https://pnpm.io/) for package management, workspaces and publishing
|
||||
- [lerna](https://lerna.js.org/) for bumping versions
|
||||
- see CONTRIBUTING.md for more info
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { cx } from '@strudel.cycles/react';
|
||||
import React from 'react';
|
||||
|
||||
function Loader({ active }) {
|
||||
return (
|
||||
<div className="overflow-hidden opacity-50 fixed top-0 left-0 w-full z-[1000]">
|
||||
<div className={cx('h-[2px] block w-full', active ? 'bg-foreground animate-train' : 'bg-transparent')}>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default Loader;
|
||||
@@ -18,6 +18,7 @@ import * as tunes from './tunes.mjs';
|
||||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||
import { themes } from './themes.mjs';
|
||||
import { settingsMap, useSettings, setLatestCode } from '../settings.mjs';
|
||||
import Loader from './Loader';
|
||||
|
||||
const { latestCode } = settingsMap.get();
|
||||
|
||||
@@ -42,7 +43,7 @@ const modules = [
|
||||
import('@strudel.cycles/csound'),
|
||||
];
|
||||
|
||||
evalScope(
|
||||
const modulesLoading = evalScope(
|
||||
controls, // sadly, this cannot be exported from core direclty
|
||||
...modules,
|
||||
);
|
||||
@@ -102,7 +103,7 @@ export function Repl({ embedded = false }) {
|
||||
const isEmbedded = embedded || window.location !== window.parent.location;
|
||||
const [view, setView] = useState(); // codemirror view
|
||||
const [lastShared, setLastShared] = useState();
|
||||
const [pending, setPending] = useState(false);
|
||||
const [pending, setPending] = useState(true);
|
||||
|
||||
const { theme, keybindings, fontSize, fontFamily } = useSettings();
|
||||
|
||||
@@ -111,16 +112,45 @@ export function Repl({ embedded = false }) {
|
||||
initialCode: '// LOADING',
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime,
|
||||
beforeEval: () => {
|
||||
beforeEval: async () => {
|
||||
setPending(true);
|
||||
await modulesLoading;
|
||||
cleanupUi();
|
||||
cleanupDraw();
|
||||
setPending(true);
|
||||
},
|
||||
afterEval: ({ code }) => {
|
||||
afterEval: async ({ code, pattern }) => {
|
||||
// preload sounds
|
||||
const t = scheduler.getTime();
|
||||
const lookahead = 16;
|
||||
const upcoming = pattern
|
||||
.queryArc(t, t + lookahead)
|
||||
.filter((h) => h.value.s)
|
||||
.map((h) => `${h.value.bank ? `${h.value.bank}_` : ''}${h.value.s}:${h.value.n || 0}`)
|
||||
.filter((v, i, all) => all.indexOf(v) === i);
|
||||
// console.log('now preloading sounds:', upcoming);
|
||||
const preload = upcoming.map(async (v) => {
|
||||
const [s, n] = v.split(':');
|
||||
const sound = soundMap.value[s];
|
||||
if (!sound) {
|
||||
throw new Error(`[preload] error: sound not found: "${s}:${n}"`);
|
||||
}
|
||||
// TODO: only preload if not already preloaded...
|
||||
// TODO: add sound.preload interface that only loads the sample, without creating a buffersource
|
||||
return sound.onTrigger(getAudioContext().currentTime + 0.5, { s, n }, () => {
|
||||
// console.log('onended');
|
||||
});
|
||||
});
|
||||
await Promise.all(preload);
|
||||
// console.log('preloading done');
|
||||
|
||||
setPending(false);
|
||||
setLatestCode(code);
|
||||
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
||||
},
|
||||
onEvalError: (err) => {
|
||||
console.log('errr');
|
||||
setPending(false);
|
||||
},
|
||||
onToggle: (play) => !play && cleanupDraw(false),
|
||||
drawContext,
|
||||
});
|
||||
@@ -135,6 +165,7 @@ export function Repl({ embedded = false }) {
|
||||
'highlight',
|
||||
);
|
||||
setCode(decoded || latestCode || randomTune);
|
||||
setPending(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -144,10 +175,14 @@ export function Repl({ embedded = false }) {
|
||||
async (e) => {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
if (getAudioContext().state !== 'running') {
|
||||
alert('please click play to initialize the audio. you can use shortcuts after that!');
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.code === 'Period') {
|
||||
} else if (e.key === '.') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
@@ -200,6 +235,7 @@ export function Repl({ embedded = false }) {
|
||||
logger(`[repl] ✨ loading random tune "${name}"`);
|
||||
clearCanvas();
|
||||
resetLoadedSounds();
|
||||
scheduler.setCps(1);
|
||||
await prebake(); // declare default samples
|
||||
await evaluate(code, false);
|
||||
};
|
||||
@@ -253,6 +289,7 @@ export function Repl({ embedded = false }) {
|
||||
// 'bg-gradient-to-t from-green-900 to-slate-900', //
|
||||
)}
|
||||
>
|
||||
<Loader active={pending} />
|
||||
<Header context={context} />
|
||||
<section className="grow flex text-gray-100 relative overflow-auto cursor-text pb-0" id="code">
|
||||
<CodeMirror
|
||||
|
||||
@@ -10,6 +10,15 @@ module.exports = {
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
train: {
|
||||
'0%': { transform: 'translateX(-100%)' },
|
||||
'100%': { transform: 'translateX(100%)' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
train: 'train 2s linear infinite',
|
||||
},
|
||||
colors: {
|
||||
// codemirror-theme settings
|
||||
background: 'var(--background)',
|
||||
|
||||
Reference in New Issue
Block a user