Merge pull request #315 from tidalcycles/astro-build

Astro build
This commit is contained in:
Felix Roos 2022-12-23 00:49:56 +01:00 committed by GitHub
commit b9122ebc34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
214 changed files with 15403 additions and 16844 deletions

View File

@ -9,6 +9,9 @@ out/**
postcss.config.js
postcss.config.cjs
tailwind.config.js
tailwind.config.cjs
vite.config.js
/**/dist/**/*
!**/*.mjs
**/*.tsx
**/*.ts

View File

@ -24,10 +24,10 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: "npm"
- name: Install Dependencies
run: npm ci && cd repl && npm ci && cd ../tutorial && npm ci
run: npm ci && cd website
- name: Build
run: npm run build
@ -39,7 +39,7 @@ jobs:
uses: actions/upload-pages-artifact@v1
with:
# Upload entire repository
path: "./out"
path: "./website/dist"
- name: Deploy to GitHub Pages
id: deployment

View File

@ -5,5 +5,6 @@
],
"yaml.schemas": {
"https://json.schemastore.org/github-workflow.json": "file:///home/felix/projects/strudel/.github/workflows/deploy.yml"
}
},
"testing.automaticallyOpenPeekView": "never"
}

View File

@ -12,8 +12,8 @@ To get in touch with the contributors, either
## Ask a Question
If you have any questions about strudel, make sure you've read the
[tutorial](https://strudel.tidalcycles.org/tutorial/) to find out if it answers your question.
If you have any questions about strudel, make sure you've glanced through the
[docs](https://strudel.tidalcycles.org/learn/) to find out if it answers your question.
If not, use one of the Communication Channels above!
Don't be afraid to ask! Your question might be of great value for other people too.
@ -29,12 +29,10 @@ If you made some music with strudel, you can give back some love and share what
Your creation could also be part of the random selection in the REPL if you want.
Use one of the Communication Channels listed above.
## Improve the Tutorial
## Improve the Docs
If you find some weak spots in the [tutorial](https://strudel.tidalcycles.org/),
you are welcome to improve them by editing [this file](https://github.com/tidalcycles/strudel/blob/main/tutorial/tutorial.mdx).
This will even work without setting up a development environment, only a github account is required.
If you find some weak spots in the [docs](https://strudel.tidalcycles.org/learn/getting-started),
you can edit each file directly on github via the "Edit this page" link located in the right sidebar.
## Propose a Feature

View File

@ -5,7 +5,7 @@
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is slowly stabilising, but please continue to tread carefully.
- Try it here: <https://strudel.tidalcycles.org/>
- Tutorial: <https://strudel.tidalcycles.org/tutorial/>
- Docs: <https://strudel.tidalcycles.org/learn/>
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
## Running Locally

View File

@ -4,16 +4,16 @@
"private": true,
"description": "Port of tidalcycles to javascript",
"scripts": {
"pretest": "cd tutorial && npm run jsdoc-json",
"pretest": "npm run jsdoc-json",
"test": "vitest run --version",
"test-ui": "vitest --ui",
"test-coverage": "vitest --coverage",
"bootstrap": "lerna bootstrap",
"setup": "npm i && npm run bootstrap && cd repl && npm i && cd ../tutorial && npm i",
"setup": "npm i && npm run bootstrap && cd website && npm i",
"snapshot": "vitest run -u --silent",
"repl": "cd repl && npm run dev",
"repl": "cd website && npm run dev",
"osc": "cd packages/osc && npm run server",
"build": "rm -rf out && cd repl && npm run build && cd ../tutorial && npm run build",
"build": "cd website && npm run build",
"preview": "npx serve ./out",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",

View File

@ -2,7 +2,7 @@ export const logKey = 'strudel.log';
export function logger(message, type, data = {}) {
console.log(`%c${message}`, 'background-color: black;color:white;border-radius:15px');
if (typeof CustomEvent !== 'undefined') {
if (typeof document !== 'undefined' && typeof CustomEvent !== 'undefined') {
document.dispatchEvent(
new CustomEvent(logKey, {
detail: {

View File

@ -32,7 +32,7 @@ yields:
## Mini Notation API
See "Mini Notation" in the [Strudel Tutorial](https://strudel.tidalcycles.org/tutorial/)
See "Mini Notation" in the [Strudel Tutorial](https://strudel.tidalcycles.org/learn/mini-notation)
## Building the Parser

View File

@ -36,4 +36,4 @@ s("<bd sd> hh").osc()
or just [click here](http://localhost:3000/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/tutorial/#superdirt-api)
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/outputs#superdirt-api)

View File

@ -31,4 +31,4 @@ yields:
## Tonal API
See "Tonal API" in the [Strudel Tutorial](https://strudel.tidalcycles.org/tutorial/)
See "Tonal API" in the [Strudel Tutorial](https://strudel.tidalcycles.org/learn/tonal)

View File

@ -1,5 +1,7 @@
# @strudel.cycles/tone
Note: This package still works but is no longer maintained in favor of `@strudel.cycles/webaudio`.
This package adds Tone.js functions to strudel Patterns.
## Install
@ -31,11 +33,3 @@ document.getElementById('play').addEventListener('click', async () => {
[open in codesandbox](https://codesandbox.io/s/strudel-tone-example-5ph2te?file=/src/index.js:0-708)
## API
See "Tone API" in the [Strudel Tutorial](https://strudel.tidalcycles.org/tutorial/)
## Dev Notes
- `@tonejs/piano` has peer dependency on `webmidi@^2.5.1`! A newer version of webmidi will break.
- If you use Tone in another package, make sure to `import { Tone } from '@strudel.cycles/tone`, to ensure you get the same AudioContext.

27
repl/.gitignore vendored
View File

@ -1,27 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
oldtunes.mjs
public/samples/EMU World/

View File

@ -1,26 +0,0 @@
# Strudel REPL
This is the REPL for Strudel. REPL stands for
- Read
- Evaluate
- Play!
- Loop
The REPL is deployed at [strudel.tidalcycles.org](https://strudel.tidalcycles.org/).
## Run REPL locally
```bash
# from project root
npm run setup
npm run repl
```
## Build REPL
```bash
cd repl
npm run build # <- builds repl + tutorial to ../docs
npm run static # <- test static build
```

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="/src/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Strudel REPL" />
<!-- TODO: add manifest images -->
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
<title>Strudel REPL</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

4463
repl/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,36 +0,0 @@
{
"name": "@strudel.cycles/repl",
"private": true,
"version": "0.0.0",
"scripts": {
"render-jsdoc": "cd ${PWD}/../tutorial/ && npm run render",
"predev": "npm run render-jsdoc",
"prebuild": "npm run render-jsdoc",
"dev": "vite --host",
"start": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run --reporter verbose -v --no-isolate",
"add-license": "cat etc/agpl-header.txt ../docs/static/js/*LICENSE.txt > /tmp/strudel-license.txt && cp /tmp/strudel-license.txt ../docs/static/js/*LICENSE.txt",
"predeploy": "npm run build",
"deploy": "gh-pages -d ../docs",
"static": "npx serve ../docs",
"dbdump": "node src/test/dbdump.js > src/test/dbdump.json"
},
"dependencies": {
"@heroicons/react": "^2.0.13",
"@supabase/supabase-js": "^1.35.3",
"nanoid": "^4.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.2",
"@vitejs/plugin-react": "^1.3.0",
"autoprefixer": "^10.4.7",
"postcss": "^8.4.13",
"rollup-plugin-visualizer": "^5.8.1",
"tailwindcss": "^3.0.24",
"vite": "^3.2.2"
}
}

View File

@ -1,12 +0,0 @@
/*
postcss.config.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/postcss.config.js>
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/>.
*/
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

View File

@ -1,11 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans',
'Droid Sans', 'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}

View File

@ -1,11 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root'),
);

View File

@ -1,33 +0,0 @@
import { Pattern, toMidi, valueToMidi } from '@strudel.cycles/core';
import { samples } from '@strudel.cycles/webaudio';
export async function prebake({ isMock = false, baseDir = '.' } = {}) {
if (!isMock) {
// https://archive.org/details/SalamanderGrandPianoV3
// License: CC-by http://creativecommons.org/licenses/by/3.0/ Author: Alexander Holm
return await Promise.all([
samples('piano.json', `${baseDir}/piano/`),
// https://github.com/sgossner/VCSL/
// https://api.github.com/repositories/126427031/contents/
// LICENSE: CC0 general-purpose
samples('vcsl.json', 'github:sgossner/VCSL/master/'),
samples('tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/'),
samples('EmuSP12.json', `${baseDir}/EmuSP12/`),
]);
}
}
const maxPan = toMidi('C8');
const panwidth = (pan, width) => pan * width + (1 - width) / 2;
Pattern.prototype.piano = function () {
return this.clip(1)
.s('piano')
.release(0.1)
.fmap((value) => {
const midi = valueToMidi(value);
// pan by pitch
const pan = panwidth(Math.min(Math.round(midi) / maxPan, 1), 0.5);
return { ...value, pan: (value.pan || 1) * pan };
});
};

View File

@ -1,11 +0,0 @@
// this script will render all example tunes and log them to the console.
// it is intended to be written to tunes.snapshot.mjs using `npm run snapshot`
import * as tunes from './tunes.mjs';
import { queryCode, testCycles } from './runtime.mjs';
Object.entries(tunes).forEach(([key, code]) => {
queryCode(code, testCycles[key] || 1).then((haps) => {
console.log(`export const ${key} = ${JSON.stringify(haps)}`);
});
});

View File

@ -1,63 +0,0 @@
/*
static.mjs - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/static.mjs>
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 { Tone } from '@strudel.cycles/tone';
import { State, TimeSpan } from '@strudel.cycles/core';
import { evaluate } from '@strudel.cycles/eval';
import { webaudioOutputTrigger } from '@strudel.cycles/webaudio';
// this is a test to play back events with as less runtime code as possible..
// the code asks for the number of seconds to prequery
// after the querying is done, the events are scheduled
// after the scheduling is done, the transport is started
// nothing happens while tone.js runs except the schedule callback, which is a thin wrapper around triggerAttackRelease calls
// so all glitches that appear here should have nothing to do with strudel and or the repl
async function playStatic(code) {
Tone.getTransport().cancel();
Tone.getTransport().stop();
let start, took;
const seconds = Number(prompt('How many seconds to run?')) || 60;
start = performance.now();
console.log('evaluating..');
const { pattern: pat } = await evaluate(code);
took = performance.now() - start;
console.log('evaluate took', took, 'ms');
console.log('querying..');
start = performance.now();
const events = pat
?.query(new State(new TimeSpan(0, seconds)))
?.filter((event) => event.part.begin.equals(event.whole.begin))
?.map((event) => ({
time: event.whole.begin.valueOf(),
duration: event.whole.end.sub(event.whole.begin).valueOf(),
value: event.value,
context: event.context,
}));
took = performance.now() - start;
console.log('query took', took, 'ms');
console.log('scheduling..');
start = performance.now();
events.forEach((event) => {
Tone.getTransport().schedule((time) => {
try {
const { onTrigger = webaudioOutputTrigger } = event.context;
onTrigger(time, event);
} catch (err) {
console.warn(err);
err.message = 'unplayable event: ' + err?.message;
console.error(err);
}
}, event.time);
});
took = performance.now() - start;
console.log('scheduling took', took, 'ms');
console.log('now starting!');
Tone.getTransport().start('+0.5');
}
export default playStatic;

View File

@ -1,29 +0,0 @@
/*
tailwind.config.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/tailwind.config.js>
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/>.
*/
module.exports = {
// TODO: find out if leaving out tutorial path works now
content: ['./src/**/*.{js,jsx,ts,tsx}', './tutorial/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {
colors: {
primary: '#c792ea',
secondary: '#c3e88d',
tertiary: '#82aaff',
highlight: '#ffcc00',
linegray: '#8a91991a',
lineblack: '#00000095',
bg: '#222222',
// header: '#8a91991a',
// footer: '#8a91991a',
header: '#00000050',
// header: 'transparent',
footer: '#00000050',
},
},
},
plugins: [require('@tailwindcss/typography')],
};

View File

@ -1,15 +0,0 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { visualizer } from 'rollup-plugin-visualizer';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
outDir: '../out',
sourcemap: true,
rollupOptions: {
plugins: [visualizer({ template: 'treemap' })],
},
},
});

View File

@ -1,6 +1,6 @@
import { queryCode } from '../../repl/src/runtime.mjs';
import { queryCode } from './runtime.mjs';
import { describe, it } from 'vitest';
import doc from '../../doc.json';
import doc from '../doc.json';
describe('runs examples', () => {
const { docs } = doc;

View File

@ -30,8 +30,7 @@ import '@strudel.cycles/xen/xen.mjs';
// import '@strudel.cycles/webaudio/webaudio.mjs';
// import '@strudel.cycles/serial/serial.mjs';
// import controls from '@strudel.cycles/core/controls.mjs';
import { prebake } from './prebake.mjs';
import '../website/src/repl/prebake';
class MockedNode {
chain() {
@ -136,8 +135,6 @@ const uiHelpersMocked = {
backgroundImage: id,
};
prebake({ isMock: true });
const canvasCtx = {
clearRect: () => {},
fillText: () => {},

View File

@ -1,5 +1,5 @@
import { queryCode, testCycles } from '../runtime.mjs';
import * as tunes from '../tunes.mjs';
import { queryCode, testCycles } from './runtime.mjs';
import * as tunes from '../website/src/repl/tunes.mjs';
import { describe, it } from 'vitest';
const tuneKeys = Object.keys(tunes);

View File

@ -1,26 +0,0 @@
import { evalScope, controls } from '@strudel.cycles/core';
import { MiniRepl as _MiniRepl } from '@strudel.cycles/react';
import { samples } from '@strudel.cycles/webaudio';
import { prebake } from '../repl/src/prebake.mjs';
fetch('https://strudel.tidalcycles.org/EmuSP12.json')
.then((res) => res.json())
.then((json) => samples(json, 'https://strudel.tidalcycles.org/EmuSP12/'));
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'),
);
// prebake();
export function MiniRepl({ tune }) {
return <_MiniRepl tune={tune} hideOutsideView={true} />;
}

View File

@ -1,41 +0,0 @@
/*
Tutorial.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/tutorial/Tutorial.js>
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 React from 'react';
import ReactDOM from 'react-dom';
import Tutorial from './tutorial.mdx';
// import ApiDoc from './ApiDoc';
import './style.scss';
import '@strudel.cycles/react/dist/style.css';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
initAudioOnFirstClick();
ReactDOM.render(
<React.StrictMode>
<div className="min-h-screen bg-slate-900">
<header className="flex-none flex justify-start sticky top-0 z-[2] w-full h-16 px-2 items-center border-b border-slate-500 text-white bg-slate-900 z-[100]">
<div className="p-4 w-full flex justify-between items-center">
<div className="flex items-center space-x-2">
<img src={'https://tidalcycles.org/img/logo.svg'} className="Tidal-logo w-10 h-10" alt="logo" />
<h1 className="text-xl cursor-pointer" onClick={() => window.scrollTo(0, 0)}>
Strudel Tutorial
</h1>
</div>
{!window.location.href.includes('localhost') && (
<div className="flex space-x-4 text-slate-200">
<a href="../">go to REPL</a>
</div>
)}
</div>
</header>
<main className="p-4 pl-6 max-w-3xl prose prose-invert">
<Tutorial />
</main>
</div>
</React.StrictMode>,
document.getElementById('root'),
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Strudel tutorial" />
<!-- TODO: add manifest images -->
<!-- <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
<title>Strudel Tutorial</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/Tutorial.jsx"></script>
</body>
</html>

10789
tutorial/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,37 +0,0 @@
{
"name": "@strudel.cycles/tutorial",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "npm run render && vite",
"start": "vite",
"build": "npm run render && vite build",
"preview": "vite preview",
"jsdoc-json": "jsdoc ../packages/ --template ../node_modules/jsdoc-json --destination ../doc.json -c ../jsdoc.config.json",
"render": "npm run jsdoc-json"
},
"type": "module",
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@mdx-js/mdx": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@tailwindcss/typography": "^0.5.2",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"@vitejs/plugin-react": "^1.3.0",
"autoprefixer": "^10.4.7",
"install": "^0.13.0",
"npm": "^8.10.0",
"postcss": "^8.4.13",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",
"remark-toc": "^8.0.1",
"sass": "^1.51.0",
"tailwindcss": "^3.0.24",
"vite": "^2.9.9",
"vite-plugin-mdx": "^3.5.10"
}
}

View File

@ -1,12 +0,0 @@
/*
postcss.config.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/postcss.config.js>
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/>.
*/
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@ -1,13 +0,0 @@
/*
tailwind.config.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/tailwind.config.js>
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/>.
*/
module.exports = {
content: ['./**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/typography')],
};

View File

@ -1,982 +0,0 @@
import { MiniRepl } from './MiniRepl';
import { JsDoc } from './JsDoc';
# Table of Contents
# What is Strudel?
With Strudel, you can expressively write dynamic music pieces.
It aims to be [Tidal Cycles](https://tidalcycles.org/) for JavaScript (started by the same author).
You don't need to know JavaScript or Tidal Cycles to make music with Strudel.
This interactive tutorial will guide you through the basics of Strudel.
The best place to actually make music with Strudel is the [Strudel REPL](https://strudel.tidalcycles.org/).
## Show me a Demo
To get a taste of what Strudel can do, check out this track:
<MiniRepl
tune={`samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
stack(
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
.speed(perlin.range(.7,.9)) // random sample speed variation
,"<a1 b1\*2 a1(3,8) e2>" // bassline
.off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps
.add(perlin.range(0,.5)) // random pitch variation
.superimpose(add(.05)) // add second, slightly detuned voice
.n() // wrap in "n"
.decay(.15).sustain(0) // make each note of equal length
.s('sawtooth') // waveform
.gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
.add(perlin.range(0,.5)) // random pitch variation
.n() // wrap in "n"
.s('sawtooth') // waveform
.gain(.16) // turn down
.cutoff(500) // fixed cutoff
.attack(1) // slowly fade in
)
.slow(3/2)`}
/>
## Disclaimer
- This project is still in its experimental state. In the future, parts of it might change significantly.
- This tutorial is far from complete.
<br />
# Playing Pitches
Pitches are an essential building block for music. In Strudel, there are 3 different options to express a pitch:
- `note`: letter notation
- `n`: number notation
- `freq`: frequency notation
## note
Notes are notated with the note letter, followed by the octave number. You can notate flats with `b` and sharps with `#`.
<MiniRepl tune={`note("a3 c#4 e4 a4")`} />
By the way, you can edit the contents of the player, and press "update" to hear your change!
You can also press "play" on the next player without needing to stop the last one.
## n
If you don't like notes, you can also use numbers with `n` instead:
<MiniRepl tune={`n("57 61 64 69")`} />
These numbers are interpreted as so called midi numbers, where adjacent whole numbers are 1 semitone apart.
You could also write decimal numbers to get microtonal pitches:
<MiniRepl tune={`n("74.5 75 75.5 76")`} />
## freq
To get maximum freedom, you can also use `freq` to directly control the frequency:
<MiniRepl tune={`freq("220 275 330 440")`} />
In this example, we play A3 (220Hz), C#4 natural (275Hz), E4 (330Hz) and A4 (440Hz).
<br />
# Playing Sounds
Instead of pitches, we can also play sounds with `s`:
<MiniRepl tune={`s("bd hh sd hh")`} />
Similarly, we can also use `s` to change the sound of our pitches:
<MiniRepl tune={`note("a3 c#4 e4 a4").s("sawtooth")`} />
Try changing the sound to `square`, `triangle` or `sine`!
We will go into the defails of sounds and synths [later](http://localhost:3000/tutorial/#web-audio-output).
<br />
# Syntax
So far, we've seen the following syntax:
```
xxx("foo").yyy("bar")
```
Generally, `xxx` and `yyy` are called functions, while `foo` and `bar` are called function arguments.
So far, we've used the functions to declare which aspect of the sound we want to control, and their arguments for the actual data.
The `yyy` function is called a chained function, because it is appended with a dot.
Strudel makes heavy use of chained functions. Here is a more extreme example:
<MiniRepl
tune={`note("a3 c#4 e4 a4")
.s("sawtooth")
.cutoff(500)
//.delay(0.5)
.room(0.5)`}
/>
The `//` is a line comment, resulting in the `delay` function being ignored.
It is a handy way to quickly turn stuff on and off. Try uncommenting this line by deleting `//`!
The good news is, that this covers 99% of the JavaScript syntax needed for Strudel!
Let's now look at the way we can express rhythms..
<br />
# Mini Notation
Similar to Tidal Cycles, Strudel has an embedded mini language that is designed to write rhythmic patterns in a short manner.
Before diving deeper into the details, here is a flavor of how the mini language looks like:
<MiniRepl
tune={`note(\`[
[
[e5 [b4 c5] d5 [c5 b4]]
[a4 [a4 c5] e5 [d5 c5]]
[b4 [~ c5] d5 e5]
[c5 a4 a4 ~]
[[~ d5] [~ f5] a5 [g5 f5]]
[e5 [~ c5] e5 [d5 c5]]
[b4 [b4 c5] d5 e5]
[c5 a4 a4 ~]
],[
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[a2 a3 a2 a3 a2 a3 b1 c2]
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]
]
]/16\`)`}
/>
The snippet above is enclosed in backticks (`), which allows you to write multi-line strings.
You can also use double quotes (") for single line mini notation.
## Sequences
We can play more notes by separating them with spaces:
<MiniRepl tune={`note("e5 b4 d5 c5")`} />
Here, those four notes are squashed into one cycle, so each note is a quarter second long.
Try adding or removing notes and notice how the tempo changes!
## Division
We can slow the sequence down by enclosing it in brackets and dividing it by a number:
<MiniRepl tune={`note("[e5 b4 d5 c5]/2")`} />
The division by two means that the sequence will be played over the course of two cycles.
You can also use decimal numbers for any tempo you like.
## Angle Brackets
Using angle brackets, we can define the sequence length based on the number of children:
<MiniRepl tune={`note("<e5 b4 d5 c5>")`} />
The above snippet is the same as:
<MiniRepl tune={`note("[e5 b4 d5 c5]/4")`} />
The advantage of the angle brackets, is that we can add more children without needing to change the number at the end.
## Multiplication
Contrary to division, a sequence can be sped up by multiplying it by a number:
<MiniRepl tune={`note("[e5 b4 d5 c5]*2")`} />
The multiplication by 2 here means that the sequence will play twice a cycle.
## Bracket Nesting
To create more interesting rhythms, you can nest sequences with brackets, like this:
<MiniRepl tune={`note("e5 [b4 c5] d5 [c5 b4]")`} />
## Rests
The "~" represents a rest:
<MiniRepl tune={`note("[b4 [~ c5] d5 e5]")`} />
## Parallel
Using commas, we can play chords:
<MiniRepl tune={`note("g3,b3,e4")`} />
To play multiple chords in a sequence, we have to wrap them in brackets:
<MiniRepl tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
## Elongation
With the "@" symbol, we can specify temporal "weight" of a sequence child:
<MiniRepl tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} />
Here, the first chord has a weight of 2, making it twice the length of the other chords. The default weight is 1.
## Replication
Using "!" we can repeat without speeding up:
<MiniRepl tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} />
In essence, the `x!n` is like a shortcut for `[x*n]@n`.
## Euclidian
Using round brackets, we can create rhythmical sub-divisions based on three parameters: beats, segments and offset.
The first parameter controls how may beats will be played.
The second parameter controls the total amount of segments the beats will be distributed over.
The third (optional) parameter controls the starting position for distributing the beats.
One popular Euclidian rhythm (going by various names, such as "Pop Clave") is "(3,8,0)" or simply "(3,8)",
resulting in a rhythmical structure of "x ~ ~ x ~ ~ x ~" (3 beats over 8 segments, starting on position 1).
<MiniRepl tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(4)`} />
<br />
# Synths, Samples & Effects
Let's take a closer look at how we can control synths, sounds and effects.
## Synths
So far, all the mini notation examples all used the same sound, which is kind of boring.
We can change the sound, using the `s` function:
<MiniRepl tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth')`} />
Here, we are wrapping our notes inside `note` and set the sound using `s`, connected by a dot.
Those functions are only 2 of many ways to alter the properties, or _params_ of a sound.
The power of patterns allows us to sequence any _param_ independently:
<MiniRepl tune={`note("c2 <eb2 <g2 g1>>").s("<sawtooth square triangle>")`} />
Now we not only pattern the notes, but the sound as well!
`sawtooth` `square` and `triangle` are the basic waveforms available in `s`.
### Envelope
You can control the envelope of a synth using the `attack`, `decay`, `sustain` and `release` functions:
<MiniRepl
tune={`note("c2 <eb2 <g2 g1>>").s('sawtooth')
.attack(.1).decay(.1).sustain(.2).release(.1)`}
/>
## Samples
Besides Synths, `s` can also play back samples:
<MiniRepl tune={`s("bd sd,hh*8,misc/2")`} />
To know which sounds are available, open the [default sample map](https://strudel.tidalcycles.org/EmuSP12.json)
### Custom Sample Maps
You can load your own sample map like this:
<MiniRepl
tune={`samples({
bd: 'bd/BT0AADA.wav',
sd: 'sd/rytm-01-classic.wav',
hh: 'hh27/000_hh27closedhh.wav',
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
s("bd sd,hh*8")`}
/>
The `samples` function takes an object that maps sound names to audio file paths.
The second argument is the base URL that comes before each path. Make sure your base URL ends with a slash, while your sample paths do **not** begin with one.
Because github is a popular choice to dump samples, there is a shortcut for that:
<MiniRepl
tune={`samples({
bd: 'bd/BT0AADA.wav',
sd: 'sd/rytm-01-classic.wav',
hh: 'hh27/000_hh27closedhh.wav',
}, 'github:tidalcycles/Dirt-Samples/master/');
s("bd sd,hh*8")`}
/>
The format is `github:user/repo/branch/`.
### Multiple Samples per Sound
It is also possible, to declare multiple files for one sound, using the array notation:
<MiniRepl
tune={`samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
s("<bd:0 bd:1>,~ <sd:0 sd:1>,[hh:0 hh:1]*2")`}
/>
The `:0` `:1` etc. are the indices of the array.
The sample number can also be set using `n`:
<MiniRepl
tune={`samples({
bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav'],
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
s("bd,~ sd,hh*4").n("<0 1>")`}
/>
### Pitched Sounds
For pitched sounds, you can use `note`, just like with synths:
<MiniRepl
tune={`samples({
'gtr': 'gtr/0001_cleanC.wav',
}, 'github:tidalcycles/Dirt-Samples/master/');
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').gain(.5)`}
/>
Here, the guitar samples will overlap, because they always play till the end.
If we want them to behave more like a synth, we can add `clip(1)`:
<MiniRepl
tune={`samples({
'gtr': 'gtr/0001_cleanC.wav',
}, 'github:tidalcycles/Dirt-Samples/master/');
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s('gtr').clip(1)
.gain(.5)`}
/>
### Base Pitch
If we have 2 samples with different base pitches, we can make them in tune by specifying the pitch like this:
<MiniRepl
tune={`samples({
'gtr': 'gtr/0001_cleanC.wav',
'moog': { 'g3': 'moog/005_Mighty%20Moog%20G3.wav' },
}, 'github:tidalcycles/Dirt-Samples/master/');
note("g3 [bb3 c4] <g4 f4 eb4 f3>@2").s("gtr,moog").clip(1)
.gain(.5)`}
/>
If a sample has no pitch set, `c3` is the default.
We can also declare different samples for different regions of the keyboard:
<MiniRepl
tune={`samples({
'moog': {
'g2': 'moog/004_Mighty%20Moog%20G2.wav',
'g3': 'moog/005_Mighty%20Moog%20G3.wav',
'g4': 'moog/006_Mighty%20Moog%20G4.wav',
}}, 'github:tidalcycles/Dirt-Samples/master/');
note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
.s('moog').clip(1)
.gain(.5)`}
/>
The sampler will always pick the closest matching sample for the current note!
## Sampler Effects
### Pattern.begin
<JsDoc name="Pattern.begin" h={0} />
### Pattern.end
<JsDoc name="Pattern.end" h={0} />
### Pattern.loopAt
<JsDoc name="Pattern.loopAt" h={0} />
### Pattern.chop
<JsDoc name="Pattern.chop" h={0} />
## Audio Effects
Wether you're using a synth or a sample, you can apply these effects:
### gain
<JsDoc name="gain" h={0} />
### velocity
<JsDoc name="velocity" h={0} />
### cutoff
<JsDoc name="cutoff" h={0} />
### resonance
<JsDoc name="resonance" h={0} />
### hcutoff
<JsDoc name="hcutoff" h={0} />
### hresonance
<JsDoc name="hresonance" h={0} />
### bandf
<JsDoc name="bandf" h={0} />
### bandq
<JsDoc name="bandq" h={0} />
### vowel
<JsDoc name="vowel" h={0} />
### pan
<JsDoc name="pan" h={0} />
### coarse
<JsDoc name="coarse" h={0} />
### shape
<JsDoc name="shape" h={0} />
### crush
<JsDoc name="crush" h={0} />
<br />
# JavaScript API
While the mini notation is powerful on its own, there is much more to discover.
Internally, the mini notation will expand to use the actual functional JavaScript API.
For example, this Pattern in Mini Notation:
<MiniRepl tune={`note("c3 eb3 g3")`} />
is equivalent to this Pattern without Mini Notation:
<MiniRepl tune={`note(seq(c3, eb3, g3))`} />
Similarly, there is an equivalent function for every aspect of the mini notation.
Which representation to use is a matter of context. As a rule of thumb, you can think of the JavaScript API
to fit better for the larger context, while mini notation is more practical for individiual rhythms.
## Limits of Mini Notation
While the Mini Notation is a powerful way to write rhythms shortly, it also has its limits. Take this example:
<MiniRepl
tune={`stack(
note("c2 eb2(3,8)").s('sawtooth').cutoff(800),
s("bd,~ sd,hh*4")
)`}
/>
Here, we are using mini notation for the individual rhythms, while using the function `stack` to mix them.
While stack is also available as `,` in mini notation, we cannot use it here, because we have different types of sounds.
## Notes
Notes are automatically available as variables:
<MiniRepl tune={`note(seq(d4, fs4, a4)) // note("d4 f#4 a4")`} />
An important difference to the mini notation:
For sharp notes, the letter "s" is used instead of "#", because JavaScript does not support "#" in a variable name.
The above is the same as:
<MiniRepl tune={`note(seq('d4', 'f#4', 'a4'))`} />
Using strings, you can also use "#".
## Alternative Syntax
In the above example, we are nesting a function inside a function, which makes reading the parens a little more difficult.
To avoid getting to many nested parens, there is an alternative syntax to add a type to a pattern:
<MiniRepl tune={`seq(d4, fs4, a4).note()`} />
You can use this with any function that declares a type (like `n`, `s`, `note`, `freq` etc), just make sure to leave the parens empty!
## Pattern Factories
The following functions will return a pattern.
<!--
### pure
<JsDoc name="pure" h={0} />
Most of the time, you won't need that function as input values of pattern creating functions are purified by default.
-->
### cat
<JsDoc name="cat" h={0} />
### seq
<JsDoc name="seq" h={0} />
### stack
<JsDoc name="stack" h={0} />
### timeCat
<JsDoc name="timeCat" h={0} />
<!-- ## polymeter
how to use?
<MiniRepl tune={`polymeter(3, e3, g3, b3)`} /> -->
<!--
see https://github.com/tidalcycles/strudel/discussions/211
### polyrhythm(...[...values])
Plays the given items at the same time, within the same length:
<MiniRepl tune={`polyrhythm([e3, g3], [e4, g4, b4])`} />
We can write the same with **stack** and **cat**:
<MiniRepl tune={`stack(seq(e3, g3), seq(e4, g4, b4))`} />
You can also use the shorthand **pr** instead of **polyrhythm**.
-->
## Combining Patterns
You can freely mix JS patterns, mini patterns and values! For example, this pattern:
<MiniRepl
tune={`cat(
stack(g3,b3,e4),
stack(a3,c3,e4),
stack(b3,d3,fs4),
stack(b3,e4,g4)
).note()`}
/>
...is equivalent to:
<MiniRepl
tune={`cat(
"g3,b3,e4",
"a3,c3,e4",
"b3,d3,f#4",
"b3,e4,g4"
).note()`}
/>
... as well as:
<MiniRepl tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} />
While mini notation is almost always shorter, it only has a handful of modifiers: \* / ! @.
When using JS patterns, there is a lot more you can do.
## Time Modifiers
The following functions modify a pattern temporal structure in some way.
### Pattern.slow
<JsDoc name="Pattern.slow" h={0} />
### Pattern.fast
<JsDoc name="Pattern.fast" h={0} />
### Pattern.early
<JsDoc name="Pattern.early" h={0} />
### Pattern.late
<JsDoc name="Pattern.late" h={0} />
### Pattern.legato
<JsDoc name="Pattern.legato" h={0} />
### Pattern.struct
<JsDoc name="Pattern.struct" h={0} />
### Pattern.euclid
<JsDoc name="Pattern.euclid" h={0} />
### Pattern.euclidLegato
<JsDoc name="Pattern.euclidLegato" h={0} />
### Pattern.rev
<JsDoc name="Pattern.rev" h={0} />
### Pattern.iter
<JsDoc name="Pattern.iter" h={0} />
### Pattern.iterBack
<JsDoc name="Pattern.iterBack" h={0} />
## Conditional Modifiers
### Pattern.every
<JsDoc name="Pattern.every" h={0} />
### Pattern.when
<JsDoc name="Pattern.when" h={0} />
## Accumulation Modifiers
### Pattern.stack
<JsDoc name="Pattern.stack" h={0} />
### Pattern.superimpose
<JsDoc name="Pattern.superimpose" h={0} />
### Pattern.layer
<JsDoc name="Pattern.layer" h={0} />
### Pattern.off
<JsDoc name="Pattern.off" h={0} />
### Pattern.echo
<JsDoc name="Pattern.echo" h={0} />
### Pattern.echoWith
<JsDoc name="Pattern.echoWith" h={0} />
## Concat Modifiers
### Pattern.seq
<JsDoc name="Pattern.seq" h={0} />
### Pattern.cat
<JsDoc name="Pattern.cat" h={0} />
## Value Modifiers
### Pattern.add
<JsDoc name="Pattern.add" h={0} />
### Pattern.sub
<JsDoc name="Pattern.sub" h={0} />
### Pattern.mul
<JsDoc name="Pattern.mul" h={0} />
### Pattern.div
<JsDoc name="Pattern.div" h={0} />
### Pattern.round
<JsDoc name="Pattern.round" h={0} />
### Pattern.apply
<JsDoc name="Pattern.apply" h={0} />
### Pattern.range
<JsDoc name="Pattern.range" h={0} />
### Pattern.chunk
<JsDoc name="Pattern.chunk" h={0} />
### Pattern.chunkBack
<JsDoc name="Pattern.chunkBack" h={0} />
## Continuous Signals
Signals are patterns with continuous values, meaning they have theoretically infinite steps.
They can provide streams of numbers that can be sampled at discrete points in time.
### saw
<JsDoc name="saw" h={0} />
### sine
<JsDoc name="sine" h={0} />
### cosine
<JsDoc name="cosine" h={0} />
### tri
<JsDoc name="tri" h={0} />
### square
<JsDoc name="square" h={0} />
### Ranges from -1 to 1
There is also `saw2`, `sine2`, `cosine2`, `tri2` and `square2` which have a range from -1 to 1!
### rand
<JsDoc name="rand" h={0} />
### perlin
<JsDoc name="perlin" h={0} />
### irand
<JsDoc name="irand" h={0} />
## Random Modifiers
These methods add random behavior to your Patterns.
### chooseCycles
<JsDoc name="chooseCycles" h={0} />
### Pattern.degradeBy
<JsDoc name="Pattern.degradeBy" h={0} />
### Pattern.degrade
<JsDoc name="Pattern.degrade" h={0} />
### Pattern.undegradeBy
<JsDoc name="Pattern.undegradeBy" h={0} />
### Pattern.sometimesBy
<JsDoc name="Pattern.sometimesBy" h={0} />
### Pattern.sometimes
<JsDoc name="Pattern.sometimes" h={0} />
### Pattern.someCyclesBy
<JsDoc name="Pattern.someCyclesBy" h={0} />
### Pattern.someCycles
<JsDoc name="Pattern.someCycles" h={0} />
### Pattern.often
<JsDoc name="Pattern.often" h={0} />
### Pattern.rarely
<JsDoc name="Pattern.rarely" h={0} />
### Pattern.almostNever
<JsDoc name="Pattern.almostNever" h={0} />
### Pattern.almostAlways
<JsDoc name="Pattern.almostAlways" h={0} />
### Pattern.never
<JsDoc name="Pattern.never" h={0} />
### Pattern.always
<JsDoc name="Pattern.always" h={0} />
<br />
<br />
# Tonal API
The Tonal API, uses [tonaljs](https://github.com/tonaljs/tonal) to provide helpers for musical operations.
### transpose(semitones)
Transposes all notes to the given number of semitones:
<MiniRepl tune={`"c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).note()`} />
This method gets really exciting when we use it with a pattern as above.
Instead of numbers, scientific interval notation can be used as well:
<MiniRepl tune={`"c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()`} />
### scale(name)
Turns numbers into notes in the scale (zero indexed). Also sets scale for other scale operations, like scaleTranpose.
<MiniRepl
tune={`"0 2 4 6 4 2"
.scale(seq('C2 major', 'C2 minor').slow(2))
.note()`}
/>
Note that the scale root is octaved here. You can also omit the octave, then index zero will default to octave 3.
All the available scale names can be found [here](https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts).
### scaleTranspose(steps)
Transposes notes inside the scale by the number of steps:
<MiniRepl
tune={`"-8 [2,4,6]"
.scale('C4 bebop major')
.scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")
.note()`}
/>
### voicings(range?)
Turns chord symbols into voicings, using the smoothest voice leading possible:
<MiniRepl tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()`} />
### rootNotes(octave = 2)
Turns chord symbols into root notes of chords in given octave.
<MiniRepl tune={`"<C^7 A7b13 Dm7 G7>".rootNotes(3).note()`} />
Together with layer, struct and voicings, this can be used to create a basic backing track:
<MiniRepl
tune={`"<C^7 A7b13 Dm7 G7>".layer(
x => x.voicings('lefthand').struct("~ x").note(),
x => x.rootNotes(2).note().s('sawtooth').cutoff(800)
)`}
/>
<!-- TODO: use range instead of octave. -->
<!-- TODO: find out why composition does not work -->
<br />
<br />
# MIDI API
Strudel also supports midi via [webmidi](https://npmjs.com/package/webmidi).
### midi(outputName?)
Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (Linux) for internal midi messages.
If no outputName is given, it uses the first midi output it finds.
<MiniRepl
tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>")
.midi()`}
/>
In the console, you will see a log of the available MIDI devices as soon as you run the code, e.g. `Midi connected! Using "Midi Through Port-0".`
# Superdirt API
In mainline tidal, the actual sound is generated via Superdirt, which runs inside Supercollider.
Strudel also supports using Superdirt as a backend, although it requires some developer tooling to run.
## Prequisites
Getting Superdirt to work with Strudel, you need to
1. install SuperCollider + sc3 plugins, see [Tidal Docs](https://tidalcycles.org/docs/) (Install Tidal) for more info.
2. install [node.js](https://nodejs.org/en/)
3. download [Strudel Repo](https://github.com/tidalcycles/strudel/) (or git clone, if you have git installed)
4. run `npm i` in the strudel directory
5. run `npm run osc` to start the osc server, which forwards OSC messages from Strudel REPL to SuperCollider
Now you're all set!
## Usage
1. Start SuperCollider, either using SuperCollider IDE or by running `sclang` in a terminal
2. Open the [Strudel REPL](https://strudel.tidalcycles.org/#cygiYmQgc2QiKS5vc2MoKQ%3D%3D)
...or test it here:
<MiniRepl tune={`s("bd sd").osc()`} />
If you now hear sound, congratulations! If not, you can get help on the [#strudel channel in the TidalCycles discord](https://discord.com/invite/HGEdXmRkzT).
### Pattern.osc
<JsDoc name="Pattern.osc" h={0} />
## Superdirt Params
The following functions can be used with superdirt:
`s n note freq channel orbit cutoff resonance hcutoff hresonance bandf bandq djf vowel cut begin end loop fadeTime speed unitA gain amp accelerate crush coarse delay lock leslie lrate lsize pan panspan pansplay room size dry shape squiz waveloss attack decay octave detune tremolodepth`
Please refer to [Tidal Docs](https://tidalcycles.org/) for more info.

View File

@ -1,30 +0,0 @@
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import _mdx from 'vite-plugin-mdx';
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
const mdx = _mdx.default || _mdx;
// `options` are passed to `@mdx-js/mdx`
const options = {
// See https://mdxjs.com/advanced/plugins
remarkPlugins: [
remarkToc,
// E.g. `remark-frontmatter`
],
rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings],
};
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), mdx(options)],
build: {
outDir: '../out/tutorial',
},
base: '/tutorial/',
});
// jsxRuntime:'classic' to prevent "jsxDevRuntime.exports.jsxDEV is not a function" for dev mode
// mode: 'development',
// react({ jsxRuntime: 'classic' }),

19
website/.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# build output
dist/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

4
website/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
website/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

181
website/README.md Normal file
View File

@ -0,0 +1,181 @@
# Strudel Website
This is the website for Strudel, deployed at [strudel.tidalcycles.org](https://strudel.tidalcycles.org/).
It includes the REPL live coding editor and the documentation site.
## Run locally
```bash
# from project root
npm run setup
npm run repl
```
## Build
```bash
cd website
npm run build # <- builds repl + tutorial to ../docs
npm run preview # <- test static build
```
# Standard Readme of Astro Starter Kit: Docs Site
```bash
npm create astro@latest -- --template docs
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/docs)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/s/github/withastro/astro/tree/latest/examples/docs)
![docs](https://user-images.githubusercontent.com/4677417/186189283-0831b9ab-d6b9-485d-8955-3057e532ab31.png)
## Features
- ✅ **Full Markdown support**
- ✅ **Responsive mobile-friendly design**
- ✅ **Sidebar navigation**
- ✅ **Search (powered by Algolia)**
- ✅ **Multi-language i18n**
- ✅ **Automatic table of contents**
- ✅ **Automatic list of contributors**
- ✅ (and, best of all) **dark mode**
## Commands Cheatsheet
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :--------------------- | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro --help` | Get help using the Astro CLI |
To deploy your site to production, check out our [Deploy an Astro Website](https://docs.astro.build/guides/deploy) guide.
## New to Astro?
Welcome! Check out [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
## Customize This Theme
edit: removed, as css styles have been replaced with tailwind styles
### Site metadata
`src/config.ts` contains several data objects that describe metadata about your site like title, description, default language, and Open Graph details. You can customize these to match your project.
## Page metadata
Astro uses frontmatter in Markdown pages to choose layouts and pass properties to those layouts. If you are using the default layout, you can customize the page in many different ways to optimize SEO and other things. For example, you can use the `title` and `description` properties to set the document title, meta title, meta description, and Open Graph description.
```markdown
---
title: Example title
description: Really cool docs example that uses Astro
layout: ../../layouts/MainLayout.astro
---
# Page content...
```
For more SEO related properties, look at `src/components/HeadSEO.astro`
### Sidebar navigation
The sidebar navigation is controlled by the `SIDEBAR` variable in your `src/config.ts` file. You can customize the sidebar by modifying this object. A default, starter navigation has already been created for you.
```ts
export const SIDEBAR = {
en: [
{ text: "Section Header", header: true },
{ text: "Introduction", link: "en/introduction" },
{ text: "Page 2", link: "en/page-2" },
{ text: "Page 3", link: "en/page-3" },
{ text: "Another Section", header: true },
{ text: "Page 4", link: "en/page-4" },
],
};
```
Note the top-level `en` key: This is needed for multi-language support. You can change it to whatever language you'd like, or add new languages as you go. More details on this below.
### Multiple Languages support
The Astro docs template supports multiple languages out of the box. The default theme only shows `en` documentation, but you can enable multi-language support features by adding a second language to your project.
To add a new language to your project, you'll want to extend the current `src/pages/[lang]/...` layout:
```diff
📂 src/pages
┣ 📂 en
┃ ┣ 📜 page-1.md
┃ ┣ 📜 page-2.md
┃ ┣ 📜 page-3.astro
+ ┣ 📂 es
+ ┃ ┣ 📜 page-1.md
+ ┃ ┣ 📜 page-2.md
+ ┃ ┣ 📜 page-3.astro
```
You'll also need to add the new language name to the `KNOWN_LANGUAGES` map in your `src/config.ts` file. This will enable your new language switcher in the site header.
```diff
// src/config.ts
export const KNOWN_LANGUAGES = {
English: 'en',
+ Spanish: 'es',
};
```
Last step: you'll need to add a new entry to your sidebar, to create the table of contents for that language. While duplicating every page might not sound ideal to everyone, this extra control allows you to create entirely custom content for every language.
> Make sure the sidebar `link` value points to the correct language!
```diff
// src/config.ts
export const SIDEBAR = {
en: [
{ text: 'Section Header', header: true, },
{ text: 'Introduction', link: 'en/introduction' },
// ...
],
+ es: [
+ { text: 'Encabezado de sección', header: true, },
+ { text: 'Introducción', link: 'es/introduction' },
+ // ...
+ ],
};
// ...
```
If you plan to use Spanish as the default language, you just need to modify the redirect path in `src/pages/index.astro`:
```diff
<script>
- window.location.pathname = `/en/introduction`;
+ window.location.pathname = `/es/introduction`;
</script>
```
You can also remove the above script and write a landing page in Spanish instead.
### What if I don't plan to support multiple languages?
That's totally fine! Not all projects need (or can support) multiple languages. You can continue to use this theme without ever adding a second language.
If that single language is not English, you can just replace `en` in directory layouts and configurations with the preferred language.
### Search (Powered by Algolia)
[Algolia](https://www.algolia.com/) offers a free service to qualified open source projects called [DocSearch](https://docsearch.algolia.com/). If you are accepted to the DocSearch program, provide your API Key & index name in `src/config.ts` and a search box will automatically appear in your site header.
Note that Aglolia and Astro are not affiliated. We have no say over acceptance to the DocSearch program.
If you'd prefer to remove Algolia's search and replace it with your own, check out the `src/components/Header.astro` component to see where the component is added.

44
website/astro.config.mjs Normal file
View File

@ -0,0 +1,44 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import react from '@astrojs/react';
import mdx from '@astrojs/mdx';
import remarkToc from 'remark-toc';
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
import tailwind from '@astrojs/tailwind';
// import { visualizer } from 'rollup-plugin-visualizer';
const options = {
// See https://mdxjs.com/advanced/plugins
remarkPlugins: [
remarkToc,
// E.g. `remark-frontmatter`
],
rehypePlugins: [rehypeSlug, rehypeAutolinkHeadings],
};
// https://astro.build/config
export default defineConfig({
integrations: [
// Enable Preact to support Preact JSX components.
preact(),
// Enable React for the Algolia search component.
react(),
mdx(options),
tailwind(),
],
site: `https://strudel.tidalcycles.org`,
});
/*
build: {
outDir: '../out',
sourcemap: true,
rollupOptions: {
plugins: [visualizer({ template: 'treemap' })],
},
},
*/

12647
website/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

42
website/package.json Normal file
View File

@ -0,0 +1,42 @@
{
"name": "@example/docs",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"check": "astro check && tsc",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@algolia/client-search": "^4.13.1",
"@astrojs/mdx": "^0.13.0",
"@astrojs/preact": "^1.2.0",
"@astrojs/react": "^1.2.2",
"@astrojs/tailwind": "^2.1.3",
"@docsearch/css": "^3.1.0",
"@docsearch/react": "^3.1.0",
"@headlessui/react": "^1.7.7",
"@heroicons/react": "^2.0.13",
"@supabase/supabase-js": "^1.35.3",
"@tailwindcss/typography": "^0.5.8",
"@types/node": "^18.0.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"astro": "^1.7.2",
"nanoid": "^4.0.0",
"preact": "^10.7.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rehype-autolink-headings": "^6.1.1",
"rehype-slug": "^5.0.1",
"remark-toc": "^8.0.1",
"tailwindcss": "^3.2.4"
},
"devDependencies": {
"html-escaper": "^3.0.3"
}
}

Some files were not shown because too many files have changed in this diff Show More