Skip to content
This repository was archived by the owner on Jun 19, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
62705da
begin uzu package
felixroos Mar 15, 2025
dbe3915
fix: lint errors
felixroos Mar 15, 2025
b71b135
uzu: stacks now work within () + write more tests
felixroos Mar 15, 2025
e3df504
rename uzu to mondo
felixroos Mar 16, 2025
e782dc0
export strudelScope for mondo to use
felixroos Mar 16, 2025
11196fb
breaking: refactor controls
felixroos Mar 16, 2025
fae59a6
feat: add mondough package to run strudel via mondo
felixroos Mar 16, 2025
b0da353
mondo highlighting
felixroos Mar 16, 2025
95526ac
fix: formatting
felixroos Mar 16, 2025
ea61627
fix: lint error
felixroos Mar 16, 2025
9020127
fix:support negative numbers
felixroos Mar 16, 2025
77ade07
mondo: slightly improve error handling
felixroos Mar 16, 2025
9b8761b
fix: top-level functions arp/arpWith
felixroos Mar 16, 2025
3c3832d
superdough: native support for rest symbols - ~ in s
felixroos Mar 16, 2025
2dd445c
mondo: support variables
felixroos Mar 16, 2025
118b619
mondo: support - ~ in plain type + simplify errors
felixroos Mar 16, 2025
40f6489
reify variables
felixroos Mar 16, 2025
7db52b3
mondo improvements:
felixroos Mar 18, 2025
129fd7d
add notes
felixroos Mar 18, 2025
88ca544
rename @strudel/mondough to @strudel/mondo
felixroos Mar 18, 2025
642ddcd
support : in mondo
felixroos Mar 19, 2025
e04f250
mondo: proper lambda functions with local scope
felixroos Mar 19, 2025
208706f
simplify mondo: ":" is just another operator
felixroos Mar 20, 2025
65a7b30
fix: markcss can now override styles (like color)
felixroos Mar 20, 2025
3657e2f
mondo: change default markcss
felixroos Mar 20, 2025
55a5f1d
transpiler cleanup
felixroos Mar 20, 2025
3505732
mondo: add .. operator
felixroos Mar 20, 2025
d2e93a9
fix: lint
felixroos Mar 20, 2025
432e8dc
mondo: support ! and @, also express seq and cat with stepcat
felixroos Mar 20, 2025
9626f3c
mondo add % for pace
felixroos Mar 20, 2025
e162956
mondo: use stepcat for curly braces
felixroos Mar 20, 2025
c691916
mondo: generic bracket names + simplify call handler + refactor mondough
felixroos Mar 20, 2025
05ae318
add sin sqr cos aliases
felixroos Mar 20, 2025
989fdfa
transpiler: add mechanism to register custom mini language
felixroos Mar 20, 2025
ac6472a
mondo: mondo as minilang (inactive)
felixroos Mar 20, 2025
c33cfc7
waveform aliases: tri, sqr, saw, sin
felixroos Mar 20, 2025
12bb82d
add chooseIn + chooseOut
felixroos Mar 21, 2025
cf1f4ec
fix: patterns without structure would error on draw
felixroos Mar 21, 2025
6fd89dd
mondo: add | and ?
felixroos Mar 21, 2025
bd93e44
mondo: fix combination of | and ,
felixroos Mar 21, 2025
5d4ef46
mondo cleanup
felixroos Mar 21, 2025
f71db4a
mondo: flip pipe order: now pining to the end of the function...
felixroos Mar 21, 2025
492271d
mondo: support $ tidal style
felixroos Mar 22, 2025
bca16cd
mondo: rename resolve_ -> desugar_
felixroos Mar 22, 2025
6d21456
mondo: allow # character in plain values (for sharps)
felixroos Mar 23, 2025
5eabcf0
mondo mode for StrudelMirror / repl / mini repl
felixroos Mar 23, 2025
51e3aa2
mondolang function for mondo repl + fix rests
felixroos Mar 23, 2025
dd5743a
mondo: bring back $ for stacking
felixroos Mar 23, 2025
3a19e23
mondo: doc
felixroos Mar 23, 2025
92b5b65
mondo: improve doc
felixroos Mar 23, 2025
222e479
mondo: more docs
felixroos Mar 23, 2025
2938bfd
mondo: add highly impractical .(. notation for local infix application
felixroos Mar 26, 2025
7716fdb
mondo: refactor
felixroos Mar 26, 2025
abc7a1e
mondo: this is the way
felixroos Mar 26, 2025
1738e4d
fix: lint
felixroos Mar 26, 2025
64a6dac
mondo: patternable function names
felixroos Mar 26, 2025
6b2b8b5
fix: test
felixroos Mar 26, 2025
e9de459
signal: remove signal synonyms for now
felixroos Mar 29, 2025
c9f5822
remove minor change
felixroos Mar 29, 2025
2e017e4
trim readme
felixroos Mar 29, 2025
c9cafa3
mondo: improve mondo package api + update readme
felixroos Mar 29, 2025
b4027fd
fix: mondo dont mutate (breaks lambdas)
felixroos Mar 30, 2025
a0fb8fb
mondo: def node
felixroos Mar 30, 2025
f2372e7
mondo: refactor evaluate function into bits
felixroos Mar 30, 2025
94a3a60
mondo: remember pair locations
felixroos Apr 1, 2025
077aac1
mondo: add some examples from sicp book
felixroos Apr 1, 2025
f6ffdd6
mondo: move +- to ops
felixroos Apr 1, 2025
aa70246
mondo: more sicp
felixroos Apr 1, 2025
39d2784
mondo: more sicp tests
felixroos Apr 2, 2025
b2588e8
fix: lint
felixroos Apr 2, 2025
60d09ad
mondo: more tests
felixroos Apr 2, 2025
989bd09
mondo: let expressions
felixroos Apr 3, 2025
d4733a3
mondo: add half interval method example
felixroos Apr 3, 2025
d5ef686
mondo: sicp 100
felixroos Apr 3, 2025
7fc21d5
mondo: rational number test
felixroos Apr 4, 2025
243adda
mondo: more sicp tests
felixroos May 1, 2025
2da03ab
Merge remote-tracking branch 'origin/main' into uzu
felixroos May 1, 2025
30fd4c5
Merge branch 'main' into uzu
felixroos May 2, 2025
e3a6444
mondo: support line comments
felixroos May 2, 2025
29e5833
tweak
felixroos May 2, 2025
60dd8bd
tweak again
felixroos May 2, 2025
ffe831d
mondo: strings now get type "string" to be discernable from plain var…
felixroos May 2, 2025
7565354
superdough: fallback to triangle when non-string is given
felixroos May 2, 2025
4208c79
fix: ! and @ operators
felixroos May 2, 2025
607a612
rename mondo package to mondolang
felixroos May 2, 2025
2951a60
fix: rests at the end
felixroos May 3, 2025
9b10dc8
change pipe symbol from '.' to '#'
yaxu May 3, 2025
1eb5f6a
format
yaxu May 3, 2025
98efd72
delint
yaxu May 3, 2025
1b3b078
format again..
yaxu May 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/codemirror/codemirror.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const codemirrorSettings = persistentAtom('codemirror-settings', defaultS
});

// https://codemirror.net/docs/guide/
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root }) {
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root, mondo }) {
const settings = codemirrorSettings.get();
const initialSettings = Object.keys(compartments).map((key) =>
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
Expand All @@ -75,7 +75,7 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
/* search(),
highlightSelectionMatches(), */
...initialSettings,
javascript(),
mondo ? [] : javascript(),
sliderPlugin,
widgetPlugin,
// indentOnInput(), // works without. already brought with javascript extension?
Expand Down Expand Up @@ -209,6 +209,7 @@ export class StrudelMirror {
},
onEvaluate: () => this.evaluate(),
onStop: () => this.stop(),
mondo: replOptions.mondo,
});
const cmEditor = this.root.querySelector('.cm-editor');
if (cmEditor) {
Expand Down
4 changes: 2 additions & 2 deletions packages/codemirror/highlight.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';
import { RangeSetBuilder, StateEffect, StateField, Prec } from '@codemirror/state';
import { Decoration, EditorView } from '@codemirror/view';

export const setMiniLocations = StateEffect.define();
Expand Down Expand Up @@ -134,5 +134,5 @@ export const isPatternHighlightingEnabled = (on, config) => {
setTimeout(() => {
updateMiniLocations(config.editor, config.miniLocations);
}, 100);
return on ? highlightExtension : [];
return on ? Prec.highest(highlightExtension) : [];
};
26 changes: 18 additions & 8 deletions packages/core/controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
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 { Pattern, register, sequence } from './pattern.mjs';
import { Pattern, register, reify } from './pattern.mjs';

export function createParam(names) {
let isMulti = Array.isArray(names);
names = !isMulti ? [names] : names;
const name = names[0];

// todo: make this less confusing
const withVal = (xs) => {
let bag;
// check if we have an object with an unnamed control (.value)
Expand All @@ -35,25 +36,34 @@ export function createParam(names) {
}
};

const func = (...pats) => sequence(...pats).withValue(withVal);

const setter = function (...pats) {
if (!pats.length) {
return this.fmap(withVal);
// todo: make this less confusing
const func = function (value, pat) {
if (!pat) {
return reify(value).withValue(withVal);
}
if (typeof value === 'undefined') {
return pat.fmap(withVal);
}
return this.set(func(...pats));
return pat.set(reify(value).withValue(withVal));
};
Pattern.prototype[name] = function (value) {
return func(value, this);
};
Pattern.prototype[name] = setter;
return func;
}

// maps control alias names to the "main" control name
const controlAlias = new Map();

export function isControlName(name) {
return controlAlias.has(name);
}

export function registerControl(names, ...aliases) {
const name = Array.isArray(names) ? names[0] : names;
let bag = {};
bag[name] = createParam(names);
controlAlias.set(name, name);
aliases.forEach((alias) => {
bag[alias] = bag[name];
controlAlias.set(alias, name);
Expand Down
3 changes: 3 additions & 0 deletions packages/core/evaluate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
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/>.
*/

export const strudelScope = {};

export const evalScope = async (...args) => {
const results = await Promise.allSettled(args);
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
Expand All @@ -18,6 +20,7 @@ export const evalScope = async (...args) => {
modules.forEach((module) => {
Object.entries(module).forEach(([name, value]) => {
globalThis[name] = value;
strudelScope[name] = value;
});
});
return modules;
Expand Down
15 changes: 9 additions & 6 deletions packages/core/pattern.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -904,22 +904,25 @@ Pattern.prototype.collect = function () {
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
* .arpWith(haps => haps[2])
* */
Pattern.prototype.arpWith = function (func) {
return this.collect()
export const arpWith = register('arpWith', (func, pat) => {
return pat
.collect()
.fmap((v) => reify(func(v)))
.innerJoin()
.withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value)));
};
});

/**
* Selects indices in in stacked notes.
* @example
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
* .arp("0 [0,2] 1 [0,2]")
* */
Pattern.prototype.arp = function (pat) {
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
};
export const arp = register(
'arp',
(indices, pat) => pat.arpWith((haps) => reify(indices).fmap((i) => haps[i % haps.length])),
false,
);

/*
* Takes a time duration followed by one or more patterns, and shifts the given patterns in time, so they are
Expand Down
5 changes: 5 additions & 0 deletions packages/core/repl.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function repl({
setInterval,
clearInterval,
id,
mondo = false,
}) {
const state = {
schedulerError: undefined,
Expand Down Expand Up @@ -180,6 +181,10 @@ export function repl({
setTime(() => scheduler.now()); // TODO: refactor?
await beforeEval?.({ code });
shouldHush && hush();

if (mondo) {
code = `mondolang\`${code}\``;
}
let { pattern, meta } = await _evaluate(code, transpiler, transpilerOptions);
if (Object.keys(pPatterns).length) {
let patterns = Object.values(pPatterns);
Expand Down
14 changes: 8 additions & 6 deletions packages/core/signal.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));

/**
* A sine signal between 0 and 1.
*
* @return {Pattern}
* @example
* n(sine.segment(16).range(0,15))
Expand Down Expand Up @@ -100,7 +99,6 @@ export const cosine2 = sine2._early(Fraction(1).div(4));

/**
* A square signal between 0 and 1.
*
* @return {Pattern}
* @example
* n(square.segment(4).range(0,7)).scale("C:minor")
Expand Down Expand Up @@ -279,26 +277,26 @@ const _rearrangeWith = (ipat, n, pat) => {
};

/**
* @name shuffle
* Slices a pattern into the given number of parts, then plays those parts in random order.
* Each part will be played exactly once per cycle.
* @name shuffle
* @example
* note("c d e f").sound("piano").shuffle(4)
* @example
* note("c d e f".shuffle(4), "g").sound("piano")
* seq("c d e f".shuffle(4), "g").note().sound("piano")
*/
export const shuffle = register('shuffle', (n, pat) => {
return _rearrangeWith(randrun(n), n, pat);
});

/**
* @name scramble
* Slices a pattern into the given number of parts, then plays those parts at random. Similar to `shuffle`,
* but parts might be played more than once, or not at all, per cycle.
* @name scramble
* @example
* note("c d e f").sound("piano").scramble(4)
* @example
* note("c d e f".scramble(4), "g").sound("piano")
* seq("c d e f".scramble(4), "g").note().sound("piano")
*/
export const scramble = register('scramble', (n, pat) => {
return _rearrangeWith(_irand(n)._segment(n), n, pat);
Expand Down Expand Up @@ -398,6 +396,10 @@ export const chooseInWith = (pat, xs) => {
*/
export const choose = (...xs) => chooseWith(rand, xs);

// todo: doc
export const chooseIn = (...xs) => chooseInWith(rand, xs);
export const chooseOut = choose;

/**
* Chooses from the given list of values (or patterns of values), according
* to the pattern that the method is called on. The pattern should be in
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/pattern.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1001,7 +1001,7 @@ describe('Pattern', () => {
});
describe('hurry', () => {
it('Can speed up patterns and sounds', () => {
sameFirst(s('a', 'b').hurry(2), s('a', 'b').fast(2).speed(2));
sameFirst(s(sequence('a', 'b')).hurry(2), s(sequence('a', 'b')).fast(2).speed(2));
});
});
/*describe('composable functions', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/draw/draw.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export class Drawer {
this.lastFrame = phase;
this.visibleHaps = (this.visibleHaps || [])
// filter out haps that are too far in the past (think left edge of screen for pianoroll)
.filter((h) => h.endClipped >= phase - lookbehind - lookahead)
.filter((h) => h.whole && h.endClipped >= phase - lookbehind - lookahead)
// add new haps with onset (think right edge bars scrolling in)
.concat(haps.filter((h) => h.hasOnset()));
const time = phase - lookahead;
Expand All @@ -175,7 +175,7 @@ export class Drawer {
// +0.1 = workaround for weird holes in query..
const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1];
// remove all future haps
this.visibleHaps = this.visibleHaps.filter((h) => h.whole.begin < t);
this.visibleHaps = this.visibleHaps.filter((h) => h.whole?.begin < t);
this.painters = []; // will get populated by .onPaint calls attached to the pattern
// query future haps
const futureHaps = scheduler.pattern.queryArc(begin, end, { painters: this.painters });
Expand Down
39 changes: 39 additions & 0 deletions packages/mondo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# mondo

a lisp-based language intended to be used as a custom dsl for patterns that can stand on its own feet.
see the `test` folder for usage examples

more info:

- [uzulang I](https://garten.salat.dev/uzu/uzulang1.html)
- [uzulang II](https://garten.salat.dev/uzu/uzulang2.html)

## Example Usage

```js
import { MondoRunner } from 'mondolang'
// define our library of functions and variables
let lib = {
add: (a, b) => a + b,
mul: (a, b) => a * b,
PI: Math.PI,
};
// this function will evaluate nodes in the syntax tree
function evaluator(node) {
// check if node is a leaf node (!= list)
if (node.type !== 'list') {
// check lib if we find a match in the lib, otherwise return value
return lib[node.value] ?? node.value;
}
// now it can only be a list..
const [fn, ...args] = node.children;
// children in a list will already be evaluated
// the first child is expected to be a function
if (typeof fn !== 'function') {
throw new Error(`"${fn}" is not a function`);
}
return fn(...args);
}
const runner = new MondoRunner({ evaluator });
const pat = runner.run('add 1 (mul 2 PI)') // 7.283185307179586
```
Loading