mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 13:13:10 -04:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e6876c282 | |||
| 011139eff8 | |||
| 184cb746f3 | |||
| 351314ccdf | |||
| e52017c0d8 | |||
| b253d437d3 | |||
| bebbe30b50 | |||
| 489d3f8d4d | |||
| ee7d783099 | |||
| 9becd0a3f0 | |||
| 9738c90b82 | |||
| f7b58f2e3e | |||
| d856e567c1 |
@@ -201,7 +201,7 @@ interfaces.
|
||||
|
||||
The Strudel REPL is available at <https://strudel.cc>,
|
||||
including an interactive tutorial. The repository is at
|
||||
<https://github.com/tidalcycles/strudel>, all the code is open source
|
||||
<https://codeberg.org/uzu/strudel/releases>, all the code is open source
|
||||
under the GPL-3.0 License.
|
||||
|
||||
# Acknowledgments
|
||||
|
||||
@@ -128,7 +128,7 @@ For the future, it is planned to integrate alternative sound engines such as Gli
|
||||
# Links
|
||||
|
||||
The Strudel REPL is available at <https://strudel.cc>, including an interactive tutorial.
|
||||
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the GPL-3.0 License.
|
||||
The repository is at <https://codeberg.org/uzu/strudel/releases>, all the code is open source under the GPL-3.0 License.
|
||||
|
||||
# Acknowledgments
|
||||
|
||||
|
||||
+916
-780
File diff suppressed because it is too large
Load Diff
@@ -451,7 +451,7 @@ While Haskell's type system makes it a great language for the ongoing developmen
|
||||
# Links
|
||||
|
||||
The Strudel REPL is available at <https://strudel.cc>, including an interactive tutorial.
|
||||
The repository is at <https://github.com/tidalcycles/strudel>, all the code is open source under the AGPL-3.0 License.
|
||||
The repository is at <https://codeberg.org/uzu/strudel>, all the code is open source under the AGPL-3.0 License.
|
||||
|
||||
# Acknowledgments
|
||||
|
||||
|
||||
@@ -1,80 +1,63 @@
|
||||
$if(false)$
|
||||
|
||||
This is a pandoc template and should not be edited.
|
||||
|
||||
$endif$
|
||||
$if(false)$ This is a pandoc template and should not be edited. $endif$
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
$for(author-meta)$
|
||||
<meta name="author" content="$author-meta$" />
|
||||
$endfor$
|
||||
$if(date-meta)$
|
||||
<meta name="date" content="$date-meta$" />
|
||||
$endif$
|
||||
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
$if(quotes)$
|
||||
<style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style>
|
||||
$endif$
|
||||
$if(highlighting-css)$
|
||||
<style type="text/css">
|
||||
$highlighting-css$
|
||||
</style>
|
||||
$endif$
|
||||
<link rel="stylesheet" href="css/iclc.css" $if(html5)$$else$type="text/css" $endif$/>
|
||||
$for(css)$
|
||||
<link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$/>
|
||||
$endfor$
|
||||
$if(math)$
|
||||
$math$
|
||||
$endif$
|
||||
$for(header-includes)$
|
||||
$header-includes$
|
||||
$endfor$
|
||||
</head>
|
||||
<body>
|
||||
$for(include-before)$
|
||||
$include-before$
|
||||
$endfor$
|
||||
$if(title)$
|
||||
<div id="$idprefix$header">
|
||||
<h1 class="title">$title$</h1>
|
||||
$if(subtitle)$
|
||||
<h1 class="subtitle">$subtitle$</h1>
|
||||
$endif$
|
||||
<ul id="authorlist">
|
||||
$for(author)$
|
||||
<li>$author$</li>
|
||||
$endfor$
|
||||
</ul>
|
||||
$if(date)$
|
||||
<h3 class="date">$date$</h3>
|
||||
$endif$
|
||||
</div>
|
||||
$endif$
|
||||
$if(toc)$
|
||||
<div id="$idprefix$TOC">
|
||||
$toc$
|
||||
</div>
|
||||
$endif$
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" $if(lang)$ lang="$lang$" xml:lang="$lang$" $endif$>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="generator" content="pandoc" />
|
||||
$for(author-meta)$
|
||||
<meta name="author" content="$author-meta$" />
|
||||
$endfor$ $if(date-meta)$
|
||||
<meta name="date" content="$date-meta$" />
|
||||
$endif$
|
||||
<title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title>
|
||||
<style type="text/css">
|
||||
code {
|
||||
white-space: pre;
|
||||
}
|
||||
</style>
|
||||
$if(quotes)$
|
||||
<style type="text/css">
|
||||
q {
|
||||
quotes: '“' '”' '‘' '’';
|
||||
}
|
||||
</style>
|
||||
$endif$ $if(highlighting-css)$
|
||||
<style type="text/css">
|
||||
$highlighting-css$
|
||||
</style>
|
||||
$endif$
|
||||
<link rel="stylesheet" href="css/iclc.css" $if(html5)$$else$type="text/css" $endif$ />
|
||||
$for(css)$
|
||||
<link rel="stylesheet" href="$css$" $if(html5)$$else$type="text/css" $endif$ />
|
||||
$endfor$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$
|
||||
</head>
|
||||
<body>
|
||||
$for(include-before)$ $include-before$ $endfor$ $if(title)$
|
||||
<div id="$idprefix$header">
|
||||
<h1 class="title">$title$</h1>
|
||||
$if(subtitle)$
|
||||
<h1 class="subtitle">$subtitle$</h1>
|
||||
$endif$
|
||||
<ul id="authorlist">
|
||||
$for(author)$
|
||||
<li>$author$</li>
|
||||
$endfor$
|
||||
</ul>
|
||||
$if(date)$
|
||||
<h3 class="date">$date$</h3>
|
||||
$endif$
|
||||
</div>
|
||||
$endif$ $if(toc)$
|
||||
<div id="$idprefix$TOC">$toc$</div>
|
||||
$endif$
|
||||
|
||||
<h2 class="abstract">Abstract</h2>
|
||||
<div id="abstract">
|
||||
$if(abstract)$
|
||||
$abstract$
|
||||
$else$
|
||||
Please provide an abstract in the metadata block at the top of your
|
||||
markdown document. Refer to template.txt for details.
|
||||
$endif$
|
||||
</div>
|
||||
<h2 class="abstract">Abstract</h2>
|
||||
<div id="abstract">
|
||||
$if(abstract)$ $abstract$ $else$ Please provide an abstract in the metadata block at the top of your markdown
|
||||
document. Refer to template.txt for details. $endif$
|
||||
</div>
|
||||
|
||||
$body$
|
||||
$for(include-after)$
|
||||
$include-after$
|
||||
$endfor$
|
||||
</body>
|
||||
$body$ $for(include-after)$ $include-after$ $endfor$
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
Each folder represents one of the @strudel/* packages [published to npm](https://www.npmjs.com/org/strudel).
|
||||
|
||||
To understand how those pieces connect, refer to the [Technical Manual](https://codeberg.org/uzu/strudel/src/branch/main/technical-manual.md) or the individual READMEs.
|
||||
To understand how those pieces connect, refer to the [Technical Manual](https://codeberg.org/uzu/strudel/wiki/Technical-Manual) or the individual READMEs.
|
||||
|
||||
Vendored
+12
@@ -8,6 +8,11 @@ import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs';
|
||||
import sonicPink, { settings as sonicPinkSettings } from './themes/sonic-pink.mjs';
|
||||
import redText, { settings as redTextSettings } from './themes/red-text.mjs';
|
||||
import greenText, { settings as greenTextSettings } from './themes/green-text.mjs';
|
||||
import archBtw, { settings as archBtwSettings } from './themes/archBtw.mjs';
|
||||
import fruitDaw, { settings as fruitDawSettings } from './themes/fruitDaw.mjs';
|
||||
|
||||
import bluescreenlight, { settings as bluescreenlightsettings } from './themes/bluescreenlight.mjs';
|
||||
|
||||
import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs';
|
||||
import atomone, { settings as atomOneSettings } from './themes/atomone.mjs';
|
||||
import aura, { settings as auraSettings } from './themes/aura.mjs';
|
||||
@@ -39,17 +44,20 @@ import { setTheme } from '@strudel/draw';
|
||||
export const themes = {
|
||||
strudelTheme,
|
||||
algoboy,
|
||||
archBtw,
|
||||
androidstudio,
|
||||
atomone,
|
||||
aura,
|
||||
bbedit,
|
||||
blackscreen,
|
||||
bluescreen,
|
||||
bluescreenlight,
|
||||
CutiePi,
|
||||
darcula,
|
||||
dracula,
|
||||
duotoneDark,
|
||||
eclipse,
|
||||
fruitDaw,
|
||||
githubDark,
|
||||
githubLight,
|
||||
greenText,
|
||||
@@ -78,10 +86,12 @@ export const themes = {
|
||||
export const settings = {
|
||||
strudelTheme: strudelThemeSettings,
|
||||
bluescreen: bluescreenSettings,
|
||||
bluescreenlight: bluescreenlightsettings,
|
||||
blackscreen: blackscreenSettings,
|
||||
whitescreen: whitescreenSettings,
|
||||
teletext: teletextSettings,
|
||||
algoboy: algoboySettings,
|
||||
archBtw: archBtwSettings,
|
||||
androidstudio: androidstudioSettings,
|
||||
atomone: atomOneSettings,
|
||||
aura: auraSettings,
|
||||
@@ -92,9 +102,11 @@ export const settings = {
|
||||
eclipse: eclipseSettings,
|
||||
CutiePi: CutiePiSettings,
|
||||
sonicPink: sonicPinkSettings,
|
||||
fruitDaw: fruitDawSettings,
|
||||
githubLight: githubLightSettings,
|
||||
githubDark: githubDarkSettings,
|
||||
greenText: greenTextSettings,
|
||||
|
||||
gruvboxDark: gruvboxDarkSettings,
|
||||
gruvboxLight: gruvboxLightSettings,
|
||||
materialDark: materialDarkSettings,
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Arch Btw
|
||||
* Modern terminal inspired theme
|
||||
* made by Jade
|
||||
*/
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from './theme-helper.mjs';
|
||||
|
||||
const hex = ['rgb(0, 0, 0)', 'rgb(82, 208, 250)', 'rgba(113, 208, 250, .4)', 'rgba(113, 208, 250, .15)'];
|
||||
|
||||
export const settings = {
|
||||
background: hex[0],
|
||||
lineBackground: 'transparent',
|
||||
foreground: hex[1],
|
||||
selection: hex[2],
|
||||
selectionMatch: hex[0],
|
||||
gutterBackground: hex[0],
|
||||
gutterForeground: hex[2],
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: hex[0],
|
||||
};
|
||||
|
||||
export default createTheme({
|
||||
theme: 'dark',
|
||||
settings,
|
||||
styles: [
|
||||
{
|
||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
||||
color: hex[1],
|
||||
},
|
||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
|
||||
{ tag: [t.comment, t.brace, t.bracket], color: hex[2] },
|
||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[1] },
|
||||
{ tag: [t.attributeName, t.number], color: hex[1] },
|
||||
{ tag: t.keyword, color: hex[1] },
|
||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
|
||||
],
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* A lighter blue screen theme
|
||||
* made by Jade
|
||||
*/
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from './theme-helper.mjs';
|
||||
|
||||
const hex = ['rgb(75, 130, 247)', 'rgb(47, 108, 246)', 'rgb(255, 255, 255)', 'rgba(255, 255, 255,.3)'];
|
||||
|
||||
export const settings = {
|
||||
background: hex[0],
|
||||
lineBackground: 'transparent',
|
||||
foreground: hex[2],
|
||||
selection: hex[3],
|
||||
selectionMatch: hex[0],
|
||||
gutterBackground: hex[0],
|
||||
gutterForeground: hex[2],
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: hex[1],
|
||||
};
|
||||
|
||||
export default createTheme({
|
||||
theme: 'dark',
|
||||
settings,
|
||||
styles: [
|
||||
{
|
||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
||||
color: hex[2],
|
||||
},
|
||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[2] },
|
||||
{ tag: [t.comment, t.bracket, t.brace, t.compareOperator], color: hex[3] },
|
||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
|
||||
{ tag: [t.attributeName, t.number], color: hex[2] },
|
||||
{ tag: t.keyword, color: hex[2] },
|
||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] },
|
||||
],
|
||||
});
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Fruit Daw
|
||||
* made by Jade
|
||||
*/
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from './theme-helper.mjs';
|
||||
|
||||
const hex = [
|
||||
'rgb(84, 93, 98)',
|
||||
'rgb(255, 255, 255)',
|
||||
'rgba(255, 255, 255, .25)',
|
||||
'rgb(67, 76, 81)',
|
||||
'rgb(186, 230, 115)',
|
||||
'rgb(252, 184, 67)',
|
||||
'rgb(124, 206, 254)',
|
||||
'rgb(83, 101, 102)',
|
||||
'rgba(46, 62, 72,.5)',
|
||||
'rgb(94, 100, 108)',
|
||||
'rgb(167, 216, 177)',
|
||||
];
|
||||
|
||||
export const settings = {
|
||||
background: hex[0],
|
||||
lineBackground: 'transparent',
|
||||
foreground: hex[10],
|
||||
selection: hex[8],
|
||||
selectionMatch: hex[0],
|
||||
gutterBackground: hex[3],
|
||||
gutterForeground: hex[2],
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: hex[3],
|
||||
};
|
||||
|
||||
export default createTheme({
|
||||
theme: 'dark',
|
||||
settings,
|
||||
styles: [
|
||||
{
|
||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
||||
color: hex[1],
|
||||
},
|
||||
{ tag: [t.bool, t.special(t.variableName)], color: hex[1] },
|
||||
{ tag: [t.comment, t.brace, t.bracket], color: hex[2] },
|
||||
{ tag: [t.variableName], color: hex[1] },
|
||||
{ tag: [t.labelName, t.propertyName, t.self, t.atom], color: hex[5] },
|
||||
{ tag: [t.attributeName, t.number], color: hex[6] },
|
||||
{ tag: t.keyword, color: hex[5] },
|
||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[4] },
|
||||
],
|
||||
});
|
||||
@@ -884,7 +884,7 @@ describe('Pattern', () => {
|
||||
);
|
||||
});
|
||||
it('Doesnt drop haps in the 9th cycle', () => {
|
||||
// fixed with https://github.com/tidalcycles/strudel/commit/72eeaf446e3d5e186d63cc0d2276f0723cde017a
|
||||
// fixed with https://codeberg.org/uzu/strudel/commit/72eeaf446e3d5e186d63cc0d2276f0723cde017a
|
||||
expect(sequence(1, 2, 3).ply(2).early(8).firstCycle().length).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ You can also pin the version like this:
|
||||
```
|
||||
|
||||
This has the advantage that your code will always work, regardless of potential breaking changes in the strudel codebase.
|
||||
See [releases](https://github.com/tidalcycles/strudel/releases) for the latest versions.
|
||||
See [releases](https://codeberg.org/uzu/strudel/releases) for the latest versions.
|
||||
|
||||
## Use Web Component
|
||||
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
This document introduces you to Strudel in a technical sense. If you just want to *use* Strudel, have a look at the [Tutorial](https://strudel.tidalcycles.org/tutorial/).
|
||||
|
||||
## Strudel Packages
|
||||
|
||||
There are different packages for different purposes. They..
|
||||
|
||||
- split up the code into smaller chunks
|
||||
- can be selectively used to implement some sort of time based system
|
||||
|
||||
Please refer to the individual README files in the [packages folder](https://codeberg.org/uzu/strudel/src/branch/main/packages)
|
||||
|
||||
## REPL
|
||||
|
||||
The [REPL](https://strudel.tidalcycles.org/) is the place where all packages come together to form a live coding system. It can also be seen as a reference implementation for users of the library.
|
||||
|
||||
More info in the [REPL README](https://codeberg.org/uzu/strudel/src/branch/main/packages/repl/README.md)
|
||||
|
||||
# High Level Overview
|
||||
|
||||
<img src="https://codeberg.org/uzu/strudel/raw/branch/talk/talk/public/strudelflow.png" width="600" />
|
||||
|
||||
## 1. End User Code
|
||||
|
||||
The End User Code is written in JavaScript with added syntax sugar. The [eval package](https://codeberg.org/uzu/strudel/src/branch/main/packages/eval#strudelcycleseval) evaluates the user code
|
||||
after a transpilation step, which resolves the syntax sugar. If you don't want the syntax sugar, you can omit the eval package and call the native javascript `eval` instead.
|
||||
|
||||
### 🍭 Syntax Sugar
|
||||
|
||||
JavaScript Transpilation = converting valid JavaScript to valid JavaScript:
|
||||
|
||||
```js
|
||||
"c3 [e3 g3]".fast(2)
|
||||
```
|
||||
|
||||
becomes
|
||||
|
||||
```js
|
||||
mini('c3 [e3 g3]')
|
||||
.withMiniLocation([1, 0, 0], [1, 11, 11]) // source location
|
||||
.fast(2);
|
||||
```
|
||||
|
||||
- double quoted strings and backtick strings are turned into `mini` calls (single quoted strings are left as is)
|
||||
- The source location is added by chaining `withMiniLocation`, which enables the real time highlighting later
|
||||
- (psuedo) variable names that look like notes (like `c4`, `bb2` or `fs3`) are turned into strings
|
||||
- support for top level await
|
||||
- operator overloading could be implemented in the future
|
||||
|
||||
This is how it works:
|
||||
|
||||
<img src="https://codeberg.org/uzu/strudel/raw/branch/talk/talk/public/shiftflow.png" width="800" />
|
||||
|
||||
- The user code is parsed with a [shift parser](https://github.com/shapesecurity/shift-parser-js), generating an AST
|
||||
- The AST is transformed to resolve the syntax sugar
|
||||
- The AST is used to generate code again (shift-codegen)
|
||||
|
||||
Shift will most likely be replaced with acorn in the future, see https://codeberg.org/uzu/strudel/issues/174
|
||||
|
||||
### Mini Notation
|
||||
|
||||
Another important part of the user code is the mini notation, which allows to express rhythms in a short manner.
|
||||
|
||||
- the mini notation is [implemented as a PEG grammar](https://codeberg.org/uzu/strudel/src/branch/main/packages/mini/krill.pegjs), living in the [mini package](https://codeberg.org/uzu/strudel/src/branch/main/packages/mini)
|
||||
- it is based on [krill](https://github.com/Mdashdotdashn/krill) by Mdashdotdashn
|
||||
- the peg grammar is used to generate a parser with [peggyjs](https://peggyjs.org/)
|
||||
- the generated parser takes a mini notation string and outputs an AST
|
||||
- the AST can then be used to construct a pattern using the regular Strudel API
|
||||
|
||||
Here's an example AST:
|
||||
|
||||
```json
|
||||
{
|
||||
"type_": "pattern",
|
||||
"arguments_": { "alignment": "h" },
|
||||
"source_": [
|
||||
{
|
||||
"type_": "element", "source_": "c3",
|
||||
"location_": { "start": { "offset": 1, "line": 1, "column": 2 }, "end": { "offset": 4, "line": 1, "column": 5 } }
|
||||
},
|
||||
{
|
||||
"type_": "element",
|
||||
"location_": { "start": { "offset": 4, "line": 1, "column": 5 }, "end": { "offset": 11, "line": 1, "column": 12 } }
|
||||
"source_": {
|
||||
"type_": "pattern", "arguments_": { "alignment": "h" },
|
||||
"source_": [
|
||||
{
|
||||
"type_": "element", "source_": "e3",
|
||||
"location_": { "start": { "offset": 5, "line": 1, "column": 6 }, "end": { "offset": 8, "line": 1, "column": 9 } }
|
||||
},
|
||||
{
|
||||
"type_": "element", "source_": "g3",
|
||||
"location_": { "start": { "offset": 8, "line": 1, "column": 9 }, "end": { "offset": 10, "line": 1, "column": 11 } }
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
which translates to `seq(c3, seq(e3, g3))`
|
||||
|
||||
## 2. Querying & Scheduling
|
||||
|
||||
When the user code has been evaluated, we hopefully get a Pattern instance, which we can use to query events from.
|
||||
These events can then be used to trigger side effects in the real world. On that note, Events are mostly called Hap(s) in the codebase, because JS already has a built in `Event` class.
|
||||
|
||||
### Querying
|
||||
|
||||
> Querying = Asking a Pattern for Events within a certain time span
|
||||
|
||||
```js
|
||||
seq('c3', ['e3', 'g3']) // <--- Pattern
|
||||
.queryArc(0, 2) // query events within 0 and 2 cycles
|
||||
.map((hap) => hap.showWhole()); // make readable
|
||||
```
|
||||
|
||||
yields
|
||||
|
||||
```js
|
||||
[
|
||||
'0/1 -> 1/2: c3', // cycle 0
|
||||
'1/2 -> 3/4: e3',
|
||||
'3/4 -> 1/1: g3',
|
||||
'1/1 -> 3/2: c3', // cycle 1
|
||||
'3/2 -> 7/4: e3',
|
||||
'7/4 -> 2/1: g3',
|
||||
];
|
||||
```
|
||||
|
||||
### 🗓️ Scheduling
|
||||
|
||||
The scheduler will query events repeatedly, creating a possibly endless loop of time slices.
|
||||
Here is a simplified example of how it works
|
||||
|
||||
```js
|
||||
let step = 0.5; // query interval in seconds
|
||||
let tick = 0; // how many intervals have passed
|
||||
let pattern = seq('c3', ['e3', 'g3']); // pattern from user
|
||||
setInterval(() => {
|
||||
const events = pattern.queryArc(tick * step, ++tick * step);
|
||||
events.forEach((event) => {
|
||||
console.log(event.showWhole());
|
||||
const o = getAudioContext().createOscillator();
|
||||
o.frequency.value = getFreq(event.value);
|
||||
o.start(event.whole.begin);
|
||||
o.stop(event.whole.begin + event.duration);
|
||||
o.connect(getAudioContext().destination);
|
||||
});
|
||||
}, step * 1000); // query each "step" seconds
|
||||
```
|
||||
|
||||
## 3. Sound Output
|
||||
|
||||
The third and last step is to use the scheduled events to make sound.
|
||||
Patterns are wrapped with param functions to compose different properties of the sound.
|
||||
|
||||
```js
|
||||
note("[c2(3,8) [<eb2 g1> bb1]]") // sets frequency
|
||||
.s("<sawtooth square>") // sound source
|
||||
.gain(.5) // turn down volume
|
||||
.cutoff(sine.range(200,1000).slow(4)) // modulated cutoff
|
||||
.slow(2)
|
||||
.out().logValues()`,
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
Here is an example Hap value with different properties:
|
||||
|
||||
```js
|
||||
{ note: 'a4', s: 'sawtooth', gain: 0.5, cutoff: 267 }
|
||||
```
|
||||
<img src="https://codeberg.org/uzu/strudel/raw/branch/talk/talk/public/waa-nodes.png" width="600" />
|
||||
|
||||
<div className="text-left">
|
||||
|
||||
- Patterns represent just values in time!
|
||||
- Suitable for any time based output (music, visuals, movement, .. ?)
|
||||
|
||||
### Supported Outputs
|
||||
|
||||
At the time of writing this doc, the following outputs are supported:
|
||||
|
||||
- Web Audio API `.out()` see [/webaudio](https://codeberg.org/uzu/strudel/src/branch/main/packages/webaudio)
|
||||
- MIDI `.midi()` see [/midi](https://codeberg.org/uzu/strudel/src/branch/main/packages/midi)
|
||||
- OSC `.osc()` see [/osc](https://codeberg.org/uzu/strudel/src/branch/main/packages/osc)
|
||||
- Serial `.serial()` see [/serial](https://codeberg.org/uzu/strudel/src/branch/main/packages/serial)
|
||||
- Tone.js `.tone()` (deprecated?) [/tone](https://codeberg.org/uzu/strudel/src/branch/main/packages/tone)
|
||||
- WebDirt `.webdirt()` (deprecated?) [/webdirt](https://codeberg.org/uzu/strudel/src/branch/main/packages/webdirt)
|
||||
- Speech `.speak()` (experimental) part of [/core](https://codeberg.org/uzu/strudel/src/branch/main/packages/core)
|
||||
|
||||
These could change, so make sure to check the [packages folder](https://codeberg.org/uzu/strudel/src/branch/main/packages).
|
||||
@@ -1,169 +0,0 @@
|
||||
---
|
||||
// fetch all commits for just this page's path
|
||||
type Props = {
|
||||
path: string;
|
||||
};
|
||||
const { path } = Astro.props as Props;
|
||||
const resolvedPath = `website/src/pages${path}.mdx`;
|
||||
const url = `https://api.github.com/repos/tidalcycles/strudel/commits?path=${resolvedPath}`;
|
||||
const commitsURL = `https://github.com/tidalcycles/strudel/commits/main/${resolvedPath}`;
|
||||
|
||||
type Commit = {
|
||||
author: {
|
||||
id: string;
|
||||
login: string;
|
||||
};
|
||||
};
|
||||
|
||||
async function getCommits(url: string) {
|
||||
try {
|
||||
const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN ?? 'hello';
|
||||
if (!token) {
|
||||
throw new Error('Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.');
|
||||
}
|
||||
|
||||
const auth = `Basic ${Buffer.from(token, 'binary').toString('base64')}`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: auth,
|
||||
'User-Agent': 'astro-docs/1.0',
|
||||
},
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`Request to fetch commits failed. Reason: ${res.statusText}
|
||||
Message: ${data.message}`,
|
||||
);
|
||||
}
|
||||
|
||||
return data as Commit[];
|
||||
} catch (e) {
|
||||
console.warn(`[error] /src/components/AvatarList.astro
|
||||
${(e as any)?.message ?? e}`);
|
||||
return [] as Commit[];
|
||||
}
|
||||
}
|
||||
|
||||
function removeDups(arr: Commit[]) {
|
||||
const map = new Map<string, Commit['author']>();
|
||||
for (let item of arr) {
|
||||
const author = item.author;
|
||||
// Deduplicate based on author.id
|
||||
//map.set(author.id, { login: author.login, id: author.id });
|
||||
author && map.set(author.id, { login: author.login, id: author.id });
|
||||
}
|
||||
|
||||
return [...map.values()];
|
||||
}
|
||||
|
||||
const data = await getCommits(url);
|
||||
const unique = removeDups(data);
|
||||
const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors
|
||||
const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors
|
||||
---
|
||||
|
||||
<!-- Thanks to @5t3ph for https://smolcss.dev/#smol-avatar-list! -->
|
||||
<div class="contributors px-4 mb-8">
|
||||
<ul class="avatar-list" style={`--avatar-count: ${recentContributors.length}`}>
|
||||
{
|
||||
recentContributors.map((item) => (
|
||||
<li>
|
||||
<a href={`https://github.com/${item.login}`}>
|
||||
<img
|
||||
alt={`Contributor ${item.login}`}
|
||||
title={`Contributor ${item.login}`}
|
||||
width="64"
|
||||
height="64"
|
||||
src={`https://avatars.githubusercontent.com/u/${item.id}`}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
{
|
||||
additionalContributors > 0 && (
|
||||
<span>
|
||||
<a href={commitsURL}>{`and ${additionalContributors} additional contributor${
|
||||
additionalContributors > 1 ? 's' : ''
|
||||
}.`}</a>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{unique.length === 0 && <a href={commitsURL}>Contributors</a>}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.avatar-list {
|
||||
--avatar-size: 2.5rem;
|
||||
--avatar-count: 3;
|
||||
|
||||
display: grid;
|
||||
list-style: none;
|
||||
/* Default to displaying most of the avatar to
|
||||
enable easier access on touch devices, ensuring
|
||||
the WCAG touch target size is met or exceeded */
|
||||
grid-template-columns: repeat(var(--avatar-count), max(44px, calc(var(--avatar-size) / 1.15)));
|
||||
/* `padding` matches added visual dimensions of
|
||||
the `box-shadow` to help create a more accurate
|
||||
computed component size */
|
||||
padding: 0.08em;
|
||||
font-size: var(--avatar-size);
|
||||
}
|
||||
|
||||
@media (any-hover: hover) and (any-pointer: fine) {
|
||||
.avatar-list {
|
||||
/* We create 1 extra cell to enable the computed
|
||||
width to match the final visual width */
|
||||
grid-template-columns: repeat(calc(var(--avatar-count) + 1), calc(var(--avatar-size) / 1.75));
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-list li {
|
||||
width: var(--avatar-size);
|
||||
height: var(--avatar-size);
|
||||
}
|
||||
|
||||
.avatar-list li:hover ~ li a,
|
||||
.avatar-list li:focus-within ~ li a {
|
||||
transform: translateX(33%);
|
||||
}
|
||||
|
||||
.avatar-list img,
|
||||
.avatar-list a {
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.avatar-list a {
|
||||
transition: transform 180ms ease-in-out;
|
||||
}
|
||||
|
||||
.avatar-list img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 0 0.05em #fff, 0 0 0 0.08em rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.avatar-list a:focus {
|
||||
outline: 2px solid transparent;
|
||||
/* Double-layer trick to work for dark and light backgrounds */
|
||||
box-shadow: 0 0 0 0.08em var(--theme-accent), 0 0 0 0.12em white;
|
||||
}
|
||||
|
||||
.contributors {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contributors > * + * {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
@@ -82,7 +82,7 @@ export default function Search() {
|
||||
appId={ALGOLIA.appId}
|
||||
apiKey={ALGOLIA.apiKey}
|
||||
getMissingResultsUrl={({ query }) => {
|
||||
return `https://github.com/tidalcycles/strudel/issues/new?title=Missing doc for ${query}`;
|
||||
return `https://codeberg.org/uzu/strudel/issues/new?title=Missing%20doc%20for${encodeURIComponent(query)}`;
|
||||
}}
|
||||
transformItems={(items) => {
|
||||
return items.map((item) => {
|
||||
|
||||
@@ -18,5 +18,4 @@ currentPage = currentPage.endsWith('/') ? currentPage.slice(0, -1) : currentPage
|
||||
<nav aria-labelledby="grid-right" class="w-64 text-foreground">
|
||||
<TableOfContents client:media="(min-width: 50em)" headings={headings} currentPage={currentPage} />
|
||||
<MoreMenu editHref={githubEditUrl} />
|
||||
<!-- <AvatarList path={currentPage} /> -->
|
||||
</nav>
|
||||
|
||||
@@ -14,7 +14,7 @@ These pages will introduce you to [Strudel](https://strudel.cc/), a web-based [l
|
||||
|
||||
# What is Strudel?
|
||||
|
||||
[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://github.com/felixroos) in 2022.
|
||||
[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://froos.cc/) in 2022.
|
||||
Tidal Cycles, also known as Tidal, is a language for [algorithmic pattern](https://algorithmicpattern.org), and though it is most commonly used for [making music](https://tidalcycles.org/docs/showcase), it can be used for any kind of pattern making activity, including [weaving](https://www.youtube.com/watch?v=TfEmEsusXjU).
|
||||
|
||||
Tidal was first implemented as a library written in the [Haskell](https://www.haskell.org/) functional programming language, and by itself it does not make any sound.
|
||||
|
||||
@@ -70,7 +70,7 @@ export function Reference() {
|
||||
<ul>
|
||||
{entry.params?.map(({ name, type, description }, i) => (
|
||||
<li key={i}>
|
||||
{name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
|
||||
{name} : {type?.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -42,7 +42,7 @@ export function WelcomeTab({ context }) {
|
||||
GNU Affero General Public License
|
||||
</a>
|
||||
. You can find the source code at{' '}
|
||||
<a href="https://github.com/tidalcycles/strudel" target="_blank">
|
||||
<a href="https://codeberg.org/uzu/strudel" target="_blank">
|
||||
github
|
||||
</a>
|
||||
. You can also find <a href="https://github.com/felixroos/dough-samples/blob/main/README.md">licensing info</a>{' '}
|
||||
|
||||
Reference in New Issue
Block a user