mirror of
https://codeberg.org/uzu/strudel
synced 2026-09-05 14:58:03 -04:00
Merge branch 'main' into iclc-slides
This commit is contained in:
@@ -74,6 +74,7 @@ export class Cyclist {
|
||||
stop() {
|
||||
logger('[cyclist] stop');
|
||||
this.clock.stop();
|
||||
this.lastEnd = 0;
|
||||
this.setStarted(false);
|
||||
}
|
||||
setPattern(pat, autostart = false) {
|
||||
|
||||
+1
-12
@@ -41,18 +41,7 @@ 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`);
|
||||
|
||||
@@ -1511,18 +1511,20 @@ function peg$parse(input, options) {
|
||||
}
|
||||
|
||||
function peg$parsemini() {
|
||||
var s0, s1, s2, s3, s4;
|
||||
var s0, s1, s2, s3, s4, s5, s6;
|
||||
|
||||
s0 = peg$currPos;
|
||||
s1 = peg$parsews();
|
||||
s2 = peg$parsequote();
|
||||
if (s2 !== peg$FAILED) {
|
||||
s3 = peg$parsestack_or_choose();
|
||||
if (s3 !== peg$FAILED) {
|
||||
s4 = peg$parsequote();
|
||||
if (s4 !== peg$FAILED) {
|
||||
s3 = peg$parsews();
|
||||
s4 = peg$parsestack_or_choose();
|
||||
if (s4 !== peg$FAILED) {
|
||||
s5 = peg$parsews();
|
||||
s6 = peg$parsequote();
|
||||
if (s6 !== peg$FAILED) {
|
||||
peg$savedPos = s0;
|
||||
s0 = peg$f19(s3);
|
||||
s0 = peg$f19(s4);
|
||||
} else {
|
||||
peg$currPos = s0;
|
||||
s0 = peg$FAILED;
|
||||
|
||||
@@ -182,7 +182,7 @@ polymeter_stack = head:sequence tail:stack_tail?
|
||||
// Experimental haskellish parser begins
|
||||
|
||||
// mini-notation = a quoted stack
|
||||
mini = ws quote sc:stack_or_choose quote
|
||||
mini = ws quote ws sc:stack_or_choose ws quote
|
||||
{ return sc; }
|
||||
|
||||
// ------------------ operators ---------------------------
|
||||
|
||||
@@ -83,7 +83,7 @@ export function MiniRepl({
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.code === 'Period') {
|
||||
} else if (e.key === '.') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
@@ -94,26 +94,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,9 +46,9 @@ function useStrudel({
|
||||
drawContext,
|
||||
transpiler,
|
||||
editPattern,
|
||||
beforeEval: ({ code }) => {
|
||||
beforeEval: async ({ code }) => {
|
||||
setCode(code);
|
||||
beforeEval?.();
|
||||
await beforeEval?.();
|
||||
},
|
||||
afterEval: (res) => {
|
||||
const { pattern: _pattern, code } = res;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
@@ -350,6 +350,8 @@ const fontFamilyOptions = {
|
||||
PressStart: 'PressStart2P',
|
||||
galactico: 'galactico',
|
||||
'we-come-in-peace': 'we-come-in-peace',
|
||||
FiraCode: 'FiraCode',
|
||||
'FiraCode-SemiBold': 'FiraCode SemiBold',
|
||||
};
|
||||
|
||||
function SettingsTab({ scheduler }) {
|
||||
|
||||
@@ -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,25 +103,29 @@ 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();
|
||||
|
||||
const { code, setCode, scheduler, evaluate, activateCode, isDirty, activeCode, pattern, started, stop, error } =
|
||||
useStrudel({
|
||||
initialCode: '// LOADING',
|
||||
initialCode: '// LOADING...',
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime,
|
||||
beforeEval: () => {
|
||||
beforeEval: async () => {
|
||||
setPending(true);
|
||||
await modulesLoading;
|
||||
cleanupUi();
|
||||
cleanupDraw();
|
||||
setPending(true);
|
||||
},
|
||||
afterEval: ({ code }) => {
|
||||
setPending(false);
|
||||
setLatestCode(code);
|
||||
window.location.hash = '#' + encodeURIComponent(btoa(code));
|
||||
},
|
||||
onEvalError: (err) => {
|
||||
setPending(false);
|
||||
},
|
||||
onToggle: (play) => !play && cleanupDraw(false),
|
||||
drawContext,
|
||||
});
|
||||
@@ -135,6 +140,7 @@ export function Repl({ embedded = false }) {
|
||||
'highlight',
|
||||
);
|
||||
setCode(decoded || latestCode || randomTune);
|
||||
setPending(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
@@ -144,10 +150,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 +210,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 +264,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
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
font-family: 'we-come-in-peace';
|
||||
src: url('/fonts/we-come-in-peace/we-come-in-peace-bb.regular.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'FiraCode';
|
||||
src: url('/fonts/FiraCode/FiraCode-Regular.ttf');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'FiraCode-SemiBold';
|
||||
src: url('/fonts/FiraCode/FiraCode-SemiBold.ttf');
|
||||
}
|
||||
|
||||
.cm-gutters {
|
||||
display: none !important;
|
||||
|
||||
@@ -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