Compare commits

..

12 Commits

Author SHA1 Message Date
Felix Roos bd6a2c6650 first draft for sound preloading 2023-03-24 20:55:29 +01:00
Felix Roos c5a2ae47d7 Merge pull request #542 from tidalcycles/loading-animation
feat: add loader bar to animate loading state
2023-03-24 12:54:20 +01:00
Felix Roos 17654b885b feat: add loader bar to animate loading state 2023-03-24 12:41:00 +01:00
Felix Roos acaf0ca0af Merge pull request #539 from tidalcycles/setcps-reset-only-on-shuffle
do not reset cps before eval #517
2023-03-23 22:39:26 +01:00
Felix Roos 6a201be1fd Merge remote-tracking branch 'origin/main' into setcps-reset-only-on-shuffle 2023-03-23 22:35:57 +01:00
Felix Roos 8617b9164e do not reset cps before eval #517 2023-03-23 22:34:25 +01:00
Felix Roos 991e5f1b3c Merge pull request #538 from tidalcycles/improve-loading
improve initial loading + wait before eval
2023-03-23 22:27:31 +01:00
Felix Roos 55c533c947 improve initial loading + wait before eval 2023-03-23 21:56:20 +01:00
Felix Roos d17543d5d9 Merge pull request #537 from tidalcycles/fix-keypress-period
fix period key for dvorak + remove duplicated code
2023-03-23 21:40:19 +01:00
Felix Roos 73169563c4 fix period key for dvorak + remove duplicated code 2023-03-23 21:37:38 +01:00
Felix Roos d351cb9f7f Merge pull request #535 from tidalcycles/update-lerna
Update lerna
2023-03-23 11:44:56 +01:00
Felix Roos 251be60630 update package publishing guide 2023-03-23 11:39:33 +01:00
9 changed files with 84 additions and 48 deletions
+8 -1
View File
@@ -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`.
+2 -13
View File
@@ -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 -21
View File
@@ -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) => {
+4 -4
View File
@@ -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 -1
View File
@@ -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);
@@ -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
+13
View File
@@ -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;
+43 -6
View File
@@ -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
+9
View File
@@ -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)',