mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-12 22:28:36 +00:00
commit
11f26c1e2c
@ -4,7 +4,7 @@ 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, sequence } from './pattern.mjs';
|
||||
import { Pattern, register, sequence } from './pattern.mjs';
|
||||
import { zipWith } from './util.mjs';
|
||||
|
||||
const controls = {};
|
||||
@ -810,4 +810,15 @@ generic_params.forEach(([names, ...aliases]) => {
|
||||
controls.createParams = (...names) =>
|
||||
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});
|
||||
|
||||
controls.adsr = register('adsr', (adsr, pat) => {
|
||||
adsr = !Array.isArray(adsr) ? [adsr] : adsr;
|
||||
const [attack, decay, sustain, release] = adsr;
|
||||
return pat.set({ attack, decay, sustain, release });
|
||||
});
|
||||
controls.ds = register('ds', (ds, pat) => {
|
||||
ds = !Array.isArray(ds) ? [ds] : ds;
|
||||
const [decay, sustain] = ds;
|
||||
return pat.set({ decay, sustain });
|
||||
});
|
||||
|
||||
export default controls;
|
||||
|
||||
@ -50,6 +50,7 @@ Pattern.prototype.pianoroll = function ({
|
||||
timeframe: timeframeProp,
|
||||
fold = 0,
|
||||
vertical = 0,
|
||||
labels = 0,
|
||||
} = {}) {
|
||||
const ctx = getDrawContext();
|
||||
const w = ctx.canvas.width;
|
||||
@ -87,7 +88,7 @@ Pattern.prototype.pianoroll = function ({
|
||||
const isActive = event.whole.begin <= t && event.whole.end > t;
|
||||
ctx.fillStyle = event.context?.color || inactive;
|
||||
ctx.strokeStyle = event.context?.color || active;
|
||||
ctx.globalAlpha = event.context.velocity ?? 1;
|
||||
ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1;
|
||||
const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange);
|
||||
let durationPx = scale(event.duration / timeExtent, 0, timeAxis);
|
||||
const value = getValue(event);
|
||||
@ -114,6 +115,14 @@ Pattern.prototype.pianoroll = function ({
|
||||
];
|
||||
}
|
||||
isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords);
|
||||
if (labels) {
|
||||
const label = event.value.note ?? event.value.s + (event.value.n ? `:${event.value.n}` : '');
|
||||
ctx.font = `${barThickness * 0.75}px monospace`;
|
||||
ctx.strokeStyle = 'black';
|
||||
ctx.fillStyle = isActive ? 'white' : 'black';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(label, ...coords);
|
||||
}
|
||||
});
|
||||
ctx.globalAlpha = 1; // reset!
|
||||
const playheadPosition = scale(-from / timeExtent, ...timeRange);
|
||||
@ -181,6 +190,7 @@ export function pianoroll({
|
||||
timeframe: timeframeProp,
|
||||
fold = 0,
|
||||
vertical = 0,
|
||||
labels = false,
|
||||
ctx,
|
||||
} = {}) {
|
||||
const w = ctx.canvas.width;
|
||||
@ -240,7 +250,7 @@ export function pianoroll({
|
||||
const color = event.value?.color || event.context?.color;
|
||||
ctx.fillStyle = color || inactive;
|
||||
ctx.strokeStyle = color || active;
|
||||
ctx.globalAlpha = event.context.velocity ?? 1;
|
||||
ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1;
|
||||
const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange);
|
||||
let durationPx = scale(event.duration / timeExtent, 0, timeAxis);
|
||||
const value = getValue(event);
|
||||
@ -267,6 +277,14 @@ export function pianoroll({
|
||||
];
|
||||
}
|
||||
isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords);
|
||||
if (labels) {
|
||||
const label = event.value.note ?? event.value.s + (event.value.n ? `:${event.value.n}` : '');
|
||||
ctx.font = `${barThickness * 0.75}px monospace`;
|
||||
ctx.strokeStyle = 'black';
|
||||
ctx.fillStyle = isActive ? 'white' : 'black';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.fillText(label, ...coords);
|
||||
}
|
||||
});
|
||||
ctx.globalAlpha = 1; // reset!
|
||||
const playheadPosition = scale(-from / timeExtent, ...timeRange);
|
||||
|
||||
@ -67,13 +67,14 @@ export const getFreq = (noteOrMidi) => {
|
||||
return midiToFreq(noteToMidi(noteOrMidi));
|
||||
};
|
||||
|
||||
const pcs = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
|
||||
/**
|
||||
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
|
||||
* @noAutocomplete
|
||||
*/
|
||||
export const midi2note = (n) => {
|
||||
const oct = Math.floor(n / 12) - 1;
|
||||
const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12];
|
||||
const pc = pcs[n % 12];
|
||||
return pc + oct;
|
||||
};
|
||||
|
||||
@ -212,3 +213,5 @@ export const splitAt = function (index, value) {
|
||||
};
|
||||
|
||||
export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i]));
|
||||
|
||||
export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
|
||||
|
||||
@ -18,16 +18,24 @@ export function MiniRepl({
|
||||
tune,
|
||||
hideOutsideView = false,
|
||||
enableKeyboard,
|
||||
onTrigger,
|
||||
drawTime,
|
||||
punchcard,
|
||||
punchcardLabels,
|
||||
onPaint,
|
||||
canvasHeight = 200,
|
||||
fontSize = 18,
|
||||
fontFamily,
|
||||
hideHeader = false,
|
||||
theme,
|
||||
keybindings,
|
||||
isLineNumbersDisplayed,
|
||||
}) {
|
||||
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
|
||||
const evalOnMount = !!drawTime;
|
||||
const drawContext = useCallback(
|
||||
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
|
||||
[drawTime],
|
||||
punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
|
||||
[punchcard],
|
||||
);
|
||||
const {
|
||||
code,
|
||||
@ -47,7 +55,18 @@ export function MiniRepl({
|
||||
} = useStrudel({
|
||||
initialCode: tune,
|
||||
defaultOutput: webaudioOutput,
|
||||
editPattern: (pat) => (punchcard ? pat.punchcard() : pat),
|
||||
editPattern: (pat, id) => {
|
||||
//pat = pat.withContext((ctx) => ({ ...ctx, id }));
|
||||
if (onTrigger) {
|
||||
pat = pat.onTrigger(onTrigger, false);
|
||||
}
|
||||
if (onPaint) {
|
||||
pat = pat.onPaint(onPaint);
|
||||
} else if (punchcard) {
|
||||
pat = pat.punchcard({ labels: punchcardLabels });
|
||||
}
|
||||
return pat;
|
||||
},
|
||||
getTime,
|
||||
evalOnMount,
|
||||
drawContext,
|
||||
@ -82,7 +101,7 @@ export function MiniRepl({
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.key === '.') {
|
||||
} else if (e.key === '.' || e.code === 'Period') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
@ -101,7 +120,7 @@ export function MiniRepl({
|
||||
// const logId = data?.pattern?.meta?.id;
|
||||
if (logId === replId) {
|
||||
setLog((l) => {
|
||||
return l.concat([e.detail]).slice(-10);
|
||||
return l.concat([e.detail]).slice(-8);
|
||||
});
|
||||
}
|
||||
}, []),
|
||||
@ -109,33 +128,46 @@ export function MiniRepl({
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-t-md bg-background border border-lineHighlight" ref={ref}>
|
||||
<div className="flex justify-between bg-lineHighlight">
|
||||
<div className="flex">
|
||||
<button
|
||||
className={cx(
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background',
|
||||
started ? 'animate-pulse' : '',
|
||||
)}
|
||||
onClick={() => togglePlay()}
|
||||
>
|
||||
<Icon type={started ? 'stop' : 'play'} />
|
||||
</button>
|
||||
<button
|
||||
className={cx(
|
||||
'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight',
|
||||
isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed',
|
||||
)}
|
||||
onClick={() => activateCode()}
|
||||
>
|
||||
<Icon type="refresh" />
|
||||
</button>
|
||||
{!hideHeader && (
|
||||
<div className="flex justify-between bg-lineHighlight">
|
||||
<div className="flex">
|
||||
<button
|
||||
className={cx(
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background',
|
||||
started ? 'animate-pulse' : '',
|
||||
)}
|
||||
onClick={() => togglePlay()}
|
||||
>
|
||||
<Icon type={started ? 'stop' : 'play'} />
|
||||
</button>
|
||||
<button
|
||||
className={cx(
|
||||
'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight',
|
||||
isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed',
|
||||
)}
|
||||
onClick={() => activateCode()}
|
||||
>
|
||||
<Icon type="refresh" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{error && <div className="text-right p-1 text-sm text-red-200">{error.message}</div>}
|
||||
</div>
|
||||
)}
|
||||
<div className="overflow-auto relative">
|
||||
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} />}
|
||||
{show && (
|
||||
<CodeMirror6
|
||||
value={code}
|
||||
onChange={setCode}
|
||||
onViewChanged={setView}
|
||||
theme={theme}
|
||||
fontFamily={fontFamily}
|
||||
fontSize={fontSize}
|
||||
keybindings={keybindings}
|
||||
isLineNumbersDisplayed={isLineNumbersDisplayed}
|
||||
/>
|
||||
)}
|
||||
{error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>}
|
||||
</div>
|
||||
{drawTime && (
|
||||
{punchcard && (
|
||||
<canvas
|
||||
id={canvasId}
|
||||
className="w-full pointer-events-none"
|
||||
|
||||
@ -29,7 +29,8 @@ function useStrudel({
|
||||
const [pattern, setPattern] = useState();
|
||||
const [started, setStarted] = useState(false);
|
||||
const isDirty = code !== activeCode;
|
||||
const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
|
||||
//const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
|
||||
const shouldPaint = useCallback((pat) => !!pat?.context?.onPaint, []);
|
||||
|
||||
// TODO: make sure this hook reruns when scheduler.started changes
|
||||
const { scheduler, evaluate, start, stop, pause, setCps } = useMemo(
|
||||
|
||||
@ -85,7 +85,12 @@ export async function initAudioOnFirstClick() {
|
||||
}
|
||||
|
||||
let delays = {};
|
||||
const maxfeedback = 0.98;
|
||||
function getDelay(orbit, delaytime, delayfeedback, t) {
|
||||
if (delayfeedback > maxfeedback) {
|
||||
logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
|
||||
}
|
||||
delayfeedback = strudel.clamp(delayfeedback, 0, 0.98);
|
||||
if (!delays[orbit]) {
|
||||
const ac = getAudioContext();
|
||||
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
|
||||
|
||||
51
pnpm-lock.yaml
generated
51
pnpm-lock.yaml
generated
@ -92,7 +92,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/core:
|
||||
dependencies:
|
||||
@ -102,7 +102,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -127,7 +127,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/core/examples/vite-vanilla-repl-cm6:
|
||||
dependencies:
|
||||
@ -155,7 +155,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/csound:
|
||||
dependencies:
|
||||
@ -171,7 +171,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/embed: {}
|
||||
|
||||
@ -204,7 +204,7 @@ importers:
|
||||
version: link:../mini
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -223,7 +223,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/mini:
|
||||
dependencies:
|
||||
@ -236,7 +236,7 @@ importers:
|
||||
version: 3.0.2
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -255,7 +255,7 @@ importers:
|
||||
version: 5.8.1
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/react:
|
||||
dependencies:
|
||||
@ -325,7 +325,7 @@ importers:
|
||||
version: 3.3.2
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/react/examples/nano-repl:
|
||||
dependencies:
|
||||
@ -380,7 +380,7 @@ importers:
|
||||
version: 3.3.2
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/serial:
|
||||
dependencies:
|
||||
@ -390,7 +390,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/soundfonts:
|
||||
dependencies:
|
||||
@ -412,7 +412,7 @@ importers:
|
||||
version: 3.3.1
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/tonal:
|
||||
dependencies:
|
||||
@ -431,7 +431,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -447,7 +447,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -469,7 +469,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -494,7 +494,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/web/examples/repl-example:
|
||||
dependencies:
|
||||
@ -504,7 +504,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/webaudio:
|
||||
dependencies:
|
||||
@ -517,7 +517,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
|
||||
packages/webdirt:
|
||||
dependencies:
|
||||
@ -533,7 +533,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -546,7 +546,7 @@ importers:
|
||||
devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.3
|
||||
version: 4.3.3(@types/node@18.16.3)
|
||||
version: 4.3.3(@types/node@18.11.18)
|
||||
vitest:
|
||||
specifier: ^0.28.0
|
||||
version: 0.28.0(@vitest/ui@0.28.0)
|
||||
@ -646,6 +646,9 @@ importers:
|
||||
canvas:
|
||||
specifier: ^2.11.2
|
||||
version: 2.11.2
|
||||
claviature:
|
||||
specifier: ^0.1.0
|
||||
version: 0.1.0
|
||||
fraction.js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
@ -4502,7 +4505,7 @@ packages:
|
||||
'@babel/plugin-transform-react-jsx-self': 7.21.0(@babel/core@7.21.5)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.19.6(@babel/core@7.21.5)
|
||||
react-refresh: 0.14.0
|
||||
vite: 4.3.3(@types/node@18.16.3)
|
||||
vite: 4.3.3(@types/node@18.11.18)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@ -5413,6 +5416,10 @@ packages:
|
||||
resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
/claviature@0.1.0:
|
||||
resolution: {integrity: sha512-Ai12axNwQ7x/F9QAj64RYKsgvi5Y33+X3GUSKAC/9s/adEws8TSSc0efeiqhKNGKBo6rT/c+CSCwSXzXxwxZzQ==}
|
||||
dev: false
|
||||
|
||||
/clean-stack@2.2.0:
|
||||
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
@ -4,16 +4,16 @@
|
||||
"version": "0.6.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev": "astro dev --host 0.0.0.0",
|
||||
"start": "astro dev",
|
||||
"check": "astro check && tsc",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview --port 3009",
|
||||
"preview": "astro preview --port 3009 --host 0.0.0.0",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/client-search": "^4.17.0",
|
||||
"@astrojs/mdx": "^0.19.0",
|
||||
"@astrojs/mdx": "^0.19.0",
|
||||
"@astrojs/react": "^2.1.1",
|
||||
"@astrojs/tailwind": "^3.1.1",
|
||||
"@docsearch/css": "^3.3.4",
|
||||
@ -43,6 +43,7 @@
|
||||
"@uiw/codemirror-themes-all": "^4.19.16",
|
||||
"astro": "^2.3.2",
|
||||
"canvas": "^2.11.2",
|
||||
"claviature": "^0.1.0",
|
||||
"fraction.js": "^4.2.0",
|
||||
"nanoid": "^4.0.2",
|
||||
"nanostores": "^0.8.1",
|
||||
|
||||
BIN
website/public/icons/strudel_icon.png
Normal file
BIN
website/public/icons/strudel_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
10
website/src/components/Box.astro
Normal file
10
website/src/components/Box.astro
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
import LightBulbIcon from '@heroicons/react/20/solid/LightBulbIcon';
|
||||
//import MusicalNoteIcon from '@heroicons/react/20/solid/MusicalNoteIcon';
|
||||
---
|
||||
|
||||
<div class="py-1 px-6 pr-10 bg-lineHighlight relative mb-4">
|
||||
<div><slot /></div>
|
||||
<LightBulbIcon className="w-5 h-5 absolute top-4 right-4" />
|
||||
<!-- <MusicalNoteIcon className="w-5 h-5 absolute top-4 right-4" /> -->
|
||||
</div>
|
||||
24
website/src/components/Claviature.jsx
Normal file
24
website/src/components/Claviature.jsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { getClaviature } from 'claviature';
|
||||
import React from 'react';
|
||||
|
||||
export default function Claviature({ options, onClick, onMouseDown, onMouseUp, onMouseLeave }) {
|
||||
const svg = getClaviature({
|
||||
options,
|
||||
onClick,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onMouseLeave,
|
||||
});
|
||||
return (
|
||||
<svg {...svg.attributes}>
|
||||
{svg.children.map((el, i) => {
|
||||
const TagName = el.name;
|
||||
return (
|
||||
<TagName key={`${el.name}-${i}`} {...el.attributes}>
|
||||
{el.value}
|
||||
</TagName>
|
||||
);
|
||||
})}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
// import { getLanguageFromURL } from '../../languages';
|
||||
import { getLanguageFromURL } from '../../languages';
|
||||
import { SIDEBAR } from '../../config';
|
||||
|
||||
type Props = {
|
||||
@ -10,7 +10,7 @@ const { currentPage } = Astro.props as Props;
|
||||
const { BASE_URL } = import.meta.env;
|
||||
let currentPageMatch = currentPage.slice(BASE_URL.length, currentPage.endsWith('/') ? -1 : undefined);
|
||||
|
||||
const langCode = 'en'; // getLanguageFromURL(currentPage);
|
||||
const langCode = getLanguageFromURL(currentPage) || 'en';
|
||||
const sidebar = SIDEBAR[langCode];
|
||||
---
|
||||
|
||||
|
||||
19
website/src/components/QA.tsx
Normal file
19
website/src/components/QA.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import ChevronDownIcon from '@heroicons/react/20/solid/ChevronDownIcon';
|
||||
import ChevronUpIcon from '@heroicons/react/20/solid/ChevronUpIcon';
|
||||
import React from 'react';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function QA({ children, q }) {
|
||||
const [visible, setVisible] = useState(false);
|
||||
return (
|
||||
<div className="py-4 px-6 pr-10 bg-lineHighlight relative mb-4">
|
||||
<div className="cursor-pointer" onClick={() => setVisible((v) => !v)}>
|
||||
<div>{q}</div>
|
||||
<a className="p-1 absolute top-4 right-4">
|
||||
{visible ? <ChevronUpIcon className="w-5 h-5" /> : <ChevronDownIcon className="w-5 h-5" />}
|
||||
</a>
|
||||
</div>
|
||||
{visible && <div>{children}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -24,6 +24,7 @@ export type Frontmatter = {
|
||||
|
||||
export const KNOWN_LANGUAGES = {
|
||||
English: 'en',
|
||||
German: 'de',
|
||||
} as const;
|
||||
export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES);
|
||||
|
||||
@ -38,25 +39,49 @@ export const ALGOLIA = {
|
||||
apiKey: 'd5044f9d21b80e7721e5b0067a8730b1',
|
||||
};
|
||||
|
||||
export type Sidebar = Record<(typeof KNOWN_LANGUAGE_CODES)[number], Record<string, { text: string; link: string }[]>>;
|
||||
export type SidebarLang = Record<string, { text: string; link: string }[]>;
|
||||
export type Sidebar = Record<(typeof KNOWN_LANGUAGE_CODES)[number], SidebarLang>;
|
||||
export const SIDEBAR: Sidebar = {
|
||||
de: {
|
||||
Workshop: [
|
||||
{ text: 'Intro', link: 'de/workshop/getting-started' },
|
||||
{ text: 'Erste Sounds', link: 'de/workshop/first-sounds' },
|
||||
{ text: 'Erste Töne', link: 'de/workshop/first-notes' },
|
||||
{ text: 'Erste Effekte', link: 'de/workshop/first-effects' },
|
||||
{ text: 'Pattern Effekte', link: 'de/workshop/pattern-effects' },
|
||||
{ text: 'Rückblick', link: 'de/workshop/recap' },
|
||||
{ text: 'Mehr Seiten auf Englisch', link: 'workshop/getting-started' },
|
||||
],
|
||||
},
|
||||
en: {
|
||||
Tutorial: [
|
||||
{ text: 'Getting Started', link: 'learn/getting-started' },
|
||||
{ text: 'Notes', link: 'learn/notes' },
|
||||
{ text: 'Sounds', link: 'learn/sounds' },
|
||||
{ text: 'Coding syntax', link: 'learn/code' },
|
||||
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
|
||||
Workshop: [
|
||||
{ text: 'Getting Started', link: 'workshop/getting-started' },
|
||||
{ text: 'First Sounds', link: 'workshop/first-sounds' },
|
||||
{ text: 'First Notes', link: 'workshop/first-notes' },
|
||||
{ text: 'First Effects', link: 'workshop/first-effects' },
|
||||
{ text: 'Pattern Effects', link: 'workshop/pattern-effects' },
|
||||
{ text: 'Recap', link: 'workshop/recap' },
|
||||
{ text: 'Workshop in German', link: 'de/workshop/getting-started' },
|
||||
],
|
||||
'Making Sound': [
|
||||
{ text: 'Samples', link: 'learn/samples' },
|
||||
{ text: 'Synths', link: 'learn/synths' },
|
||||
{ text: 'Audio Effects', link: 'learn/effects' },
|
||||
{ text: 'MIDI & OSC', link: 'learn/input-output' },
|
||||
],
|
||||
More: [
|
||||
{ text: 'Mini-Notation', link: 'learn/mini-notation' },
|
||||
{ text: 'Coding syntax', link: 'learn/code' },
|
||||
{ text: 'Offline', link: 'learn/pwa' },
|
||||
{ text: 'Patterns', link: 'technical-manual/patterns' },
|
||||
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
|
||||
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
|
||||
{ text: 'Music metadata', link: 'learn/metadata' },
|
||||
{ text: 'CSound', link: 'learn/csound' },
|
||||
],
|
||||
'Pattern Functions': [
|
||||
{ text: 'Introduction', link: 'functions/intro' },
|
||||
{ text: 'Pattern Constructors', link: 'learn/factories' },
|
||||
{ text: 'Creating Patterns', link: 'learn/factories' },
|
||||
{ text: 'Time Modifiers', link: 'learn/time-modifiers' },
|
||||
{ text: 'Control Parameters', link: 'functions/value-modifiers' },
|
||||
{ text: 'Signals', link: 'learn/signals' },
|
||||
@ -64,14 +89,6 @@ export const SIDEBAR: Sidebar = {
|
||||
{ text: 'Accumulation', link: 'learn/accumulation' },
|
||||
{ text: 'Tonal Modifiers', link: 'learn/tonal' },
|
||||
],
|
||||
More: [
|
||||
{ text: 'MIDI & OSC', link: 'learn/input-output' },
|
||||
{ text: 'Offline', link: 'learn/pwa' },
|
||||
{ text: 'Patterns', link: 'technical-manual/patterns' },
|
||||
{ text: 'Pattern Alignment', link: 'technical-manual/alignment' },
|
||||
{ text: 'Strudel vs Tidal', link: 'learn/strudel-vs-tidal' },
|
||||
{ text: 'Music metadata', link: 'learn/metadata' },
|
||||
],
|
||||
Development: [
|
||||
{ text: 'REPL', link: 'technical-manual/repl' },
|
||||
{ text: 'Sounds', link: 'technical-manual/sounds' },
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
.cm-activeLine {
|
||||
.cm-activeLine,
|
||||
.cm-activeLineGutter {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
@ -7,3 +8,11 @@
|
||||
border: 1px solid var(--lineHighlight);
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.cm-scroller {
|
||||
font-family: inherit !important;
|
||||
}
|
||||
|
||||
.cm-gutters {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { evalScope, controls } from '@strudel.cycles/core';
|
||||
import { evalScope, controls, noteToMidi } from '@strudel.cycles/core';
|
||||
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { prebake } from '../repl/prebake';
|
||||
import { themes, settings } from '../repl/themes.mjs';
|
||||
import './MiniRepl.css';
|
||||
import { useSettings } from '../settings.mjs';
|
||||
import Claviature from '@components/Claviature';
|
||||
|
||||
let modules;
|
||||
if (typeof window !== 'undefined') {
|
||||
@ -27,9 +28,20 @@ if (typeof window !== 'undefined') {
|
||||
prebake();
|
||||
}
|
||||
|
||||
export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
|
||||
export function MiniRepl({
|
||||
tune,
|
||||
drawTime,
|
||||
punchcard,
|
||||
punchcardLabels = true,
|
||||
span = [0, 4],
|
||||
canvasHeight = 100,
|
||||
hideHeader,
|
||||
claviature,
|
||||
claviatureLabels,
|
||||
}) {
|
||||
const [Repl, setRepl] = useState();
|
||||
const { theme } = useSettings();
|
||||
const { theme, keybindings, fontSize, fontFamily, isLineNumbersDisplayed } = useSettings();
|
||||
const [activeNotes, setActiveNotes] = useState([]);
|
||||
useEffect(() => {
|
||||
// we have to load this package on the client
|
||||
// because codemirror throws an error on the server
|
||||
@ -42,11 +54,39 @@ export function MiniRepl({ tune, drawTime, punchcard, canvasHeight = 100 }) {
|
||||
<Repl
|
||||
tune={tune}
|
||||
hideOutsideView={true}
|
||||
drawTime={drawTime}
|
||||
drawTime={claviature ? [0, 0] : drawTime}
|
||||
punchcard={punchcard}
|
||||
punchcardLabels={punchcardLabels}
|
||||
span={span}
|
||||
canvasHeight={canvasHeight}
|
||||
theme={themes[theme]}
|
||||
hideHeader={hideHeader}
|
||||
keybindings={keybindings}
|
||||
fontFamily={fontFamily}
|
||||
fontSize={fontSize}
|
||||
isLineNumbersDisplayed={isLineNumbersDisplayed}
|
||||
onPaint={
|
||||
claviature
|
||||
? (ctx, time, haps, drawTime) => {
|
||||
const active = haps
|
||||
.map((hap) => hap.value.note)
|
||||
.filter(Boolean)
|
||||
.map((n) => (typeof n === 'string' ? noteToMidi(n) : n));
|
||||
setActiveNotes(active);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
{claviature && (
|
||||
<Claviature
|
||||
options={{
|
||||
range: ['C2', 'C6'],
|
||||
scaleY: 0.75,
|
||||
colorize: [{ keys: activeNotes, color: 'steelblue' }],
|
||||
labels: claviatureLabels || {},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<pre>{tune}</pre>
|
||||
|
||||
336
website/src/pages/de/workshop/first-effects.mdx
Normal file
336
website/src/pages/de/workshop/first-effects.mdx
Normal file
@ -0,0 +1,336 @@
|
||||
---
|
||||
title: Erste Effekte
|
||||
layout: ../../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../../docs/MiniRepl';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# Erste Effekte
|
||||
|
||||
import Box from '@components/Box.astro';
|
||||
|
||||
## Ein paar grundlegende Effekte
|
||||
|
||||
**low-pass filter**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth").lpf(800)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
lpf = **l**ow **p**ass **f**ilter
|
||||
|
||||
- Ändere `lpf` in 200. Hörst du wie der Bass dumpfer klingt? Es klingt so ähnlich als würde die Musik hinter einer geschlossenen Tür laufen 🚪
|
||||
- Lass uns nun die Tür öffnen: Ändere `lpf` in 5000. Der Klang wird dadurch viel heller und schärfer ✨🪩
|
||||
|
||||
</Box>
|
||||
|
||||
**filter automatisieren**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth").lpf("200 1000")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
- Füg noch mehr `lpf` Werte hinzu
|
||||
- Das pattern in `lpf` ändert nicht den Rhythmus der Bassline
|
||||
|
||||
Später sehen wir wie man mit Wellenformen Dinge automatisieren kann.
|
||||
|
||||
</Box>
|
||||
|
||||
**vowel = Vokal**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
|
||||
.sound("sawtooth").vowel("<a e i o>/2")`}
|
||||
/>
|
||||
|
||||
**gain = Verstärkung**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
sound("hh*8").gain("[.25 1]*2"),
|
||||
sound("bd*2,~ sd:1")
|
||||
) `}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Bei Rhythmen ist die Dynamik (= Veränderungen der Lautstärke) sehr wichtig.
|
||||
|
||||
- Entferne `.gain(...)` und achte darauf wie es viel flacher klingt.
|
||||
- Mach es rückgängig (strg+z dann strg+enter)
|
||||
|
||||
</Box>
|
||||
|
||||
**stacks in stacks**
|
||||
|
||||
Lass uns die obigen Beispiele kombinieren:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
stack(
|
||||
sound("hh*8").gain("[.25 1]*2"),
|
||||
sound("bd*2,~ sd:1")
|
||||
),
|
||||
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth").lpf("200 1000"),
|
||||
note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
|
||||
.sound("sawtooth").vowel("<a e i o>/2")
|
||||
) `}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Versuche die einzelnen Teile innerhalb `stack` zu erkennen, schau dir an wie die Kommas gesetzt sind.
|
||||
|
||||
Die 3 Teile (Drums, Bass, Akkorde) sind genau wie vorher, nur in einem `stack`, getrennt durch Kommas
|
||||
|
||||
</Box>
|
||||
|
||||
**Den Sound formen mit ADSR Hüllkurve**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<c3 bb2 f3 eb3>")
|
||||
.sound("sawtooth").lpf(600)
|
||||
.attack(.1)
|
||||
.decay(.1)
|
||||
.sustain(.25)
|
||||
.release(.2)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Versuche herauszufinden was die Zahlen machen. Probier folgendes:
|
||||
|
||||
- attack: `.5` vs `0`
|
||||
- decay: `.5` vs `0`
|
||||
- sustain: `1` vs `.25` vs `0`
|
||||
- release: `0` vs `.5` vs `1`
|
||||
|
||||
Kannst du erraten was die einzelnen Werte machen?
|
||||
|
||||
</Box>
|
||||
|
||||
<QA q="Lösung anzeigen" client:visible>
|
||||
|
||||
- attack (anschlagen): Zeit des Aufbaus
|
||||
- decay (zerfallen): Zeit des Abfalls
|
||||
- sustain (erhalten): Lautstärke nach Abfall
|
||||
- release (loslassen): Zeit des Abfalls nach dem Ende
|
||||
|
||||

|
||||
|
||||
</QA>
|
||||
|
||||
**adsr Kurznotation**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<c3 bb2 f3 eb3>")
|
||||
.sound("sawtooth").lpf(600)
|
||||
.adsr(".1:.1:.5:.2")
|
||||
`}
|
||||
/>
|
||||
|
||||
**delay = Verzögerung**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
|
||||
.sound("gm_electric_guitar_muted"),
|
||||
sound("<bd rim>").bank("RolandTR707")
|
||||
).delay(".5")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Probier verschiedene `delay` Werte zwischen 0 und 1. Übrigens: `.5` ist kurz für `0.5`.
|
||||
|
||||
Was passiert wenn du `.delay(".8:.125")` schreibst? Kannst du erraten was die zweite Zahl macht?
|
||||
|
||||
Was passiert wenn du `.delay(".8:.06:.8")` schreibst? Kannst du erraten was die dritte Zahl macht?
|
||||
|
||||
</Box>
|
||||
|
||||
<QA q="Lösung anzeigen" client:visible>
|
||||
|
||||
`delay("a:b:c")`:
|
||||
|
||||
- a: Lautstärke des Delays
|
||||
- b: Verzögerungszeit
|
||||
- c: Feedback (je kleiner desto schneller verschwindet das Delay)
|
||||
|
||||
</QA>
|
||||
|
||||
**room aka reverb = Hall**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Spiel mit verschiedenen Werten.
|
||||
|
||||
Füg auch ein Delay hinzu!
|
||||
|
||||
</Box>
|
||||
|
||||
**kleiner dub tune**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
|
||||
.sound("gm_electric_guitar_muted").delay(.5),
|
||||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2).gain(.5)
|
||||
)`}
|
||||
/>
|
||||
|
||||
Für echten Dub fehlt noch der Bass:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
|
||||
.sound("gm_electric_guitar_muted").delay(.5),
|
||||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2).gain(.4),
|
||||
n("<0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~>*2")
|
||||
.scale("D2:minor")
|
||||
.sound("sawtooth,triangle").lpf(800)
|
||||
)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Füg `.hush()` ans ende eines Patterns im stack...
|
||||
|
||||
</Box>
|
||||
|
||||
**pan = Panorama**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("numbers:1 numbers:2 numbers:3 numbers:4")
|
||||
.pan("0 0.3 .6 1")
|
||||
.slow(2)`}
|
||||
/>
|
||||
|
||||
**speed = Geschwindigkeit**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd rim").speed("<1 2 -1 -2>").room(.2)`} />
|
||||
|
||||
**fast and slow = schnell und langsam**
|
||||
|
||||
Mit `fast` und `slow` kann man das tempo eines patterns außerhalb der Mini-Notation ändern:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd*2,~ rim").slow(2)`} />
|
||||
|
||||
<Box>
|
||||
|
||||
Ändere den `slow` Wert. Tausche `slow` durch `fast`.
|
||||
|
||||
Was passiert wenn du den Wert automatisierst? z.b. `.fast("<1 [2 4]>")` ?
|
||||
|
||||
</Box>
|
||||
|
||||
Übrigens, innerhalb der Mini-Notation, `fast` ist `*` und `slow` ist `/`.
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("[bd*2,~ rim]*<1 [2 4]>")`} />
|
||||
|
||||
## Automation mit Signalen
|
||||
|
||||
Anstatt Werte schrittweise zu automatisieren können wir auch sogenannte Signale benutzen:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh*16").gain(sine)`} punchcard punchcardLabels={false} />
|
||||
|
||||
<Box>
|
||||
|
||||
Die grundlegenden Wellenformen sind `sine`, `saw`, `square`, `tri` 🌊
|
||||
|
||||
Probiere auch die zufälligen Signale `rand` und `perlin`!
|
||||
|
||||
Der `gain`-Wert (Verstärkung) wird in der Visualisierung als Transparenz dargestellt.
|
||||
|
||||
</Box>
|
||||
|
||||
**Bereich ändern mit `range`**
|
||||
|
||||
Signale bewegen sich standardmäßig zwischen 0 und 1. Wir können das mit `range` ändern:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh*8").lpf(saw.range(500, 2000))`} />
|
||||
|
||||
`range` ist nützlich wenn wir Funktionen mit einem anderen Wertebereich als 0 und 1 automatisieren wollen (z.b. lpf)
|
||||
|
||||
<Box>
|
||||
|
||||
Was passiert wenn du die beiden Werte vertauschst?
|
||||
|
||||
</Box>
|
||||
|
||||
Wir können die Geschwindigkeit der Automation mit slow / fast ändern:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth")
|
||||
.lpf(sine.range(100, 2000).slow(8))`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Die ganze Automation braucht nun 8 cycle bis sie sich wiederholt.
|
||||
|
||||
</Box>
|
||||
|
||||
## Rückblick
|
||||
|
||||
| name | example |
|
||||
| ----- | -------------------------------------------------------------------------------------------------- |
|
||||
| lpf | <MiniRepl hideHeader client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
|
||||
| vowel | <MiniRepl hideHeader client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
|
||||
| gain | <MiniRepl hideHeader client:visible tune={`s("hh*8").gain("[.25 1]*2")`} /> |
|
||||
| delay | <MiniRepl hideHeader client:visible tune={`s("bd rim").delay(.5)`} /> |
|
||||
| room | <MiniRepl hideHeader client:visible tune={`s("bd rim").room(.5)`} /> |
|
||||
| pan | <MiniRepl hideHeader client:visible tune={`s("bd rim").pan("0 1")`} /> |
|
||||
| speed | <MiniRepl hideHeader client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
|
||||
| range | <MiniRepl hideHeader client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
|
||||
|
||||
Lass uns nun die für Tidal typischen [Pattern Effekte anschauen](/de/workshop/pattern-effects).
|
||||
408
website/src/pages/de/workshop/first-notes.mdx
Normal file
408
website/src/pages/de/workshop/first-notes.mdx
Normal file
@ -0,0 +1,408 @@
|
||||
---
|
||||
title: Erste Töne
|
||||
layout: ../../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import { midi2note } from '@strudel.cycles/core/';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# Erste Töne
|
||||
|
||||
Jetzt schauen wir uns an wie man mit Tönen mit der `note` Funktion spielt.
|
||||
|
||||
## Zahlen und Noten
|
||||
|
||||
**Töne mit Zahlen**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("48 52 55 59").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(
|
||||
Array(49)
|
||||
.fill()
|
||||
.map((_, i) => [midi2note(i + 36), i + 36]),
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Probiere verschiedene Zahlen aus!
|
||||
|
||||
Versuch auch mal Kommazahlen, z.B. 55.5 (beachte die englische Schreibweise von Kommazahlen mit "." anstatt ",")
|
||||
|
||||
</Box>
|
||||
|
||||
**Töne mit Buchstaben**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c e g b").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(['c3', 'd3', 'e3', 'f3', 'g3', 'a3', 'b3'].map((n) => [n, n.split('')[0]]))}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Versuch verschiedene Buchstaben aus (a - g).
|
||||
|
||||
Findest du Melodien die auch gleichzeitig ein Wort sind? Tipp: ☕ 🙈 🧚
|
||||
|
||||
</Box>
|
||||
|
||||
**Vorzeichen**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("db eb gb ab bb").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(
|
||||
['db3', 'eb3', 'gb3', 'ab3', 'bb3'].map((n) => [n, n.split('').slice(0, 2).join('')]),
|
||||
)}
|
||||
/>
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c# d# f# g# a#").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(
|
||||
['c#3', 'd#3', 'f#3', 'g#3', 'a#3'].map((n) => [n, n.split('').slice(0, 2).join('')]),
|
||||
)}
|
||||
/>
|
||||
|
||||
**Andere Oktaven**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c2 e3 g4 b5").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(['c1', 'c2', 'c3', 'c4', 'c5'].map((n) => [n, n]))}
|
||||
claviatureLabels={Object.fromEntries(
|
||||
Array(49)
|
||||
.fill()
|
||||
.map((_, i) => [midi2note(i + 36), midi2note(i + 36)]),
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Probiere verschiedene Oktaven aus (1-8)
|
||||
|
||||
</Box>
|
||||
|
||||
Normalerweise kommen Leute die keine Noten besser mit Zahlen anstatt mit Buchstaben zurecht.
|
||||
Daher benutzen die folgenden Beispiele meistens Zahlen.
|
||||
Später sehen wir auch noch ein paar Tricks die es uns erleichtern Töne zu spielen die zueinander passen.
|
||||
|
||||
## Den Sound verändern
|
||||
|
||||
Genau wie bei geräuschhaften Sounds können wir den Klang unserer Töne mit `sound` verändern:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("36 43, 52 59 62 64").sound("piano")`} />
|
||||
|
||||
<Box>
|
||||
|
||||
Probier ein paar sounds aus:
|
||||
|
||||
- gm_electric_guitar_muted - E-Gitarre
|
||||
- gm_acoustic_bass - Kontrabass
|
||||
- gm_voice_oohs - Chords
|
||||
- gm_blown_bottle - Flasche
|
||||
- sawtooth - Sägezahn-Welle
|
||||
- square - Rechteck-Welle
|
||||
- triangle - Dreieck-Welle
|
||||
- Was ist mit bd, sd oder hh?
|
||||
- Entferne `.sound('...')` komplett
|
||||
|
||||
</Box>
|
||||
|
||||
**Zwischen Sounds hin und her wechseln**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("48 67 63 [62, 58]")
|
||||
.sound("piano gm_electric_guitar_muted")`}
|
||||
/>
|
||||
|
||||
**Gleichzeitige Sounds**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("48 67 63 [62, 58]")
|
||||
.sound("piano, gm_electric_guitar_muted")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Die patterns in `note` und `sound` werden kombiniert!
|
||||
|
||||
Wir schauen uns später noch mehr Möglichkeiten an wie man patterns kombiniert.
|
||||
|
||||
</Box>
|
||||
|
||||
## Längere Sequenzen
|
||||
|
||||
**Sequenzen verlangsamen mit `/`**
|
||||
|
||||
{/* [c2 bb1 f2 eb2] */}
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("[36 34 41 39]/4").sound("gm_acoustic_bass")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Das `/4` spielt die Sequenz 4 mal so langsam, also insgesamt 4 cycles = 4s.
|
||||
|
||||
Jede Note ist nun also 1s lang.
|
||||
|
||||
Schreib noch mehr Töne in die Klammern und achte darauf dass es schneller wird.
|
||||
|
||||
</Box>
|
||||
|
||||
Wenn eine Sequenz unabhängig von ihrem Inhalt immer gleich schnell bleiben soll, gibt es noch eine andere Art Klammern:
|
||||
|
||||
**Eins pro Cycle per \< \>**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("<36 34 41 39>").sound("gm_acoustic_bass")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Füg noch mehr Töne hinzu und achte darauf wie das Tempo gleich bleibt!
|
||||
|
||||
Tatsächlich sind diese Klammern nur eine Abkürzung:
|
||||
|
||||
`<a b c>` = `[a b c]/3`
|
||||
|
||||
`<a b c d>` = `[a b c d]/4`
|
||||
|
||||
usw..
|
||||
|
||||
</Box>
|
||||
|
||||
**Eine Sequenz pro Cycle**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[36 48] [34 46] [41 53] [39 51]>")
|
||||
.sound("gm_acoustic_bass")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
oder auch...
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[36 48]*4 [34 46]*4 [41 53]*4 [39 51]*4>/2")
|
||||
.sound("gm_acoustic_bass")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Alternativen**
|
||||
|
||||
Ähnlich wie Unter-Sequenzen, kann auch `<...>` innerhalb einer Sequenz verwendet werden:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("60 <63 62 65 63>")
|
||||
.sound("gm_xylophone")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
Das ist auch praktisch für atonale Sounds:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("bd*2, ~ <sd cp>, [~ hh]*2")
|
||||
.bank("RolandTR909")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
## Skalen
|
||||
|
||||
Es kann mühsam sein die richtigen Noten zu finden wenn man alle zur Verfügung hat.
|
||||
Mit Skalen ist das einfacher:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("0 2 4 <[6,8] [7,9]>")
|
||||
.scale("C:minor").sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Probier verschiedene Zahlen aus. Jede klingt gut!
|
||||
|
||||
Probier verschiedene Skalen:
|
||||
|
||||
- C:major
|
||||
- A2:minor
|
||||
- D:dorian
|
||||
- G:mixolydian
|
||||
- A2:minor:pentatonic
|
||||
- F:major:pentatonic
|
||||
|
||||
</Box>
|
||||
|
||||
**Automatisierte Skalen**
|
||||
|
||||
Wie alle Funktionen können auch Skalen mit einem Pattern automatisiert werden:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<0 -3>, 2 4 <[6,8] [7,9]>")
|
||||
.scale("<C:major D:mixolydian>/4")
|
||||
.sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Wenn du keine Ahnung hast was die Skalen bedeuten, keine Sorge.
|
||||
Es sind einfach nur Namen für verschiedene Gruppen von Tönen die gut zusammenpassen.
|
||||
|
||||
Nimm dir Zeit um herauszufinden welche Skalen du magst.
|
||||
|
||||
</Box>
|
||||
|
||||
## Wiederholen und Verlängern
|
||||
|
||||
**Verlängern mit @**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("c@3 eb").sound("gm_acoustic_bass")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Ein Element ohne `@` ist gleichbedeutend mit `@1`. Im Beispiel ist `c` drei Einheiten lang, `eb` nur eine.
|
||||
|
||||
Spiel mit der Länge!
|
||||
|
||||
</Box>
|
||||
|
||||
**Unter-Sequenzen verlängern**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<[4@2 4] [5@2 5] [6@2 6] [5@2 5]>*2")
|
||||
.scale("<C2:mixolydian F2:mixolydian>/4")
|
||||
.sound("gm_acoustic_bass")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Dieser Groove wird auch `shuffle` genannt.
|
||||
Jeder Schlag enthält 2 Töne, wobei der erste doppelt so lang wie der zweite ist.
|
||||
Das nennt man auch manchmal `triolen swing`. Es ist ein typischer Rhythmus im Blues und Jazz.
|
||||
|
||||
</Box>
|
||||
|
||||
**Wiederholen**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("c!2 [eb,<g a bb a>]").sound("piano")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Wechsel zwischen `!`, `*` und `@` hin und her.
|
||||
|
||||
Was ist der Unterschied?
|
||||
|
||||
</Box>
|
||||
|
||||
## Rückblick
|
||||
|
||||
Das haben wir in diesem Kapitel gelernt:
|
||||
|
||||
| Concept | Syntax | Example |
|
||||
| ------------ | ------ | ------------------------------------------------------------------- |
|
||||
| Verlangsamen | \/ | <MiniRepl hideHeader client:visible tune={`note("[c a f e]/2")`} /> |
|
||||
| Alternativen | \<\> | <MiniRepl hideHeader client:visible tune={`note("c <e g>")`} /> |
|
||||
| Verlängern | @ | <MiniRepl hideHeader client:visible tune={`note("c@3 e")`} /> |
|
||||
| Wiederholen | ! | <MiniRepl hideHeader client:visible tune={`note("c!3 e")`} /> |
|
||||
|
||||
Neue Funktionen:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | --------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| note | Tonhöhe als Buchstabe oder Zahl | <MiniRepl hideHeader client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| scale | Interpretiert `n` als Skalenstufe | <MiniRepl hideHeader client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| stack | Spiele mehrere Patterns parallel (s.u.) | <MiniRepl hideHeader client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
|
||||
|
||||
## Beispiele
|
||||
|
||||
**Bassline**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("gm_synth_bass_1")
|
||||
.lpf(800) // <-- we'll learn about this soon`}
|
||||
/>
|
||||
|
||||
**Melodie**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n(\`<
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
[~ 0] 1 [0 1] [~ 1]
|
||||
[~ 0] 3 [0 3] [~ 3]
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
>*2\`).scale("C4:minor")
|
||||
.sound("gm_synth_strings_1")`}
|
||||
/>
|
||||
|
||||
**Drums**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("bd*2, ~ <sd cp>, [~ hh]*2")
|
||||
.bank("RolandTR909")`}
|
||||
/>
|
||||
|
||||
**Wenn es doch nur einen Weg gäbe das alles gleichzeitig zu spielen.......**
|
||||
|
||||
<Box>
|
||||
|
||||
Das geht mit `stack` 😙
|
||||
|
||||
</Box>
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("gm_synth_bass_1").lpf(800),
|
||||
n(\`<
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
[~ 0] 1 [0 1] [~ 1]
|
||||
[~ 0] 3 [0 3] [~ 3]
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
>*2\`).scale("C4:minor")
|
||||
.sound("gm_synth_strings_1"),
|
||||
sound("bd*2, ~ <sd cp>, [~ hh]*2")
|
||||
.bank("RolandTR909")
|
||||
)`}
|
||||
/>
|
||||
|
||||
Das hört sich doch langsam nach echter Musik an!
|
||||
Wir haben Sounds, wir haben Töne.. noch ein Puzzleteil fehlt: [Effekte](/de/workshop/first-effects)
|
||||
371
website/src/pages/de/workshop/first-sounds.mdx
Normal file
371
website/src/pages/de/workshop/first-sounds.mdx
Normal file
@ -0,0 +1,371 @@
|
||||
---
|
||||
title: Erste Sounds
|
||||
layout: ../../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# Erste Sounds
|
||||
|
||||
Dies ist das erste Kapitel im Strudel Workshop, schön dich an Bord zu haben!
|
||||
|
||||
## Textfelder
|
||||
|
||||
Der Workshop ist voller interaktiver Textfelder. Lass uns lernen wie sie funktionieren. Hier ist eins:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("casio")`} />
|
||||
|
||||
<Box>
|
||||
|
||||
1. ⬆️ Klicke in das obige Textfeld ⬆️
|
||||
2. Drücke `Strg`+`Enter` zum Abspielen
|
||||
3. Ändere `casio` in `metal`
|
||||
4. Drücke `Strg`+`Enter` zum Aktualisieren
|
||||
5. Drücke `Strg`+`Punkt` zum Stoppen
|
||||
|
||||
Mac: `Strg` = `control` oder auch `option`
|
||||
|
||||
</Box>
|
||||
|
||||
Glückwunsch, du kannst jetzt live coden!
|
||||
|
||||
## Sounds
|
||||
|
||||
Gerade haben wir schon den ersten sound mit `sound` abgespielt:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("casio")`} />
|
||||
|
||||
<Box>
|
||||
|
||||
`casio` ist einer von vielen Standard Sounds.
|
||||
|
||||
Probier ein paar andere Sounds aus:
|
||||
|
||||
```
|
||||
insect wind jazz metal east crow casio space numbers
|
||||
```
|
||||
|
||||
Es kann sein, dass du kurz nichts hörst während ein neuer Sound lädt.
|
||||
|
||||
</Box>
|
||||
|
||||
**Sample Nummer ändern mit :**
|
||||
|
||||
Ein Sound kann mehrere Samples (Audio Dateien) enthalten.
|
||||
|
||||
Du kannst ein anderes Sample wählen, indem du `:` und eine Zahl an den Sound-Namen anhängst:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("casio:1")`} hideHeader />
|
||||
|
||||
<Box>
|
||||
|
||||
Probiere verschiedene Sound / Zahlen Kombinationen.
|
||||
|
||||
Ohne Zahl ist gleichbedeutend mit `:0`
|
||||
|
||||
</Box>
|
||||
|
||||
Jetzt weißt du wie man Sounds abspielt und ihre Sample Nummer einstellt.
|
||||
Vorerst bleiben wir bei den voreingestellten Sounds, später erfahren wir noch wie man eigene benutzt.
|
||||
|
||||
## Drum Sounds
|
||||
|
||||
Strudel kommt von Haus aus mit einer breiten Auswahl an Drum Sounds:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh sd oh")`} hideHeader />
|
||||
|
||||
<Box>
|
||||
|
||||
Diese 2-Buchstaben Kombinationen stehen für verschiedene Teile eines Schlagzeugs:
|
||||
|
||||
- `bd` = **b**ass **d**rum - Basstrommel
|
||||
- `sd` = **s**nare **d**rum - Schnarrtrommel
|
||||
- `rim` = **rim**shot - Rahmenschlag
|
||||
- `hh` = **h**i**h**at - Hallo Hut
|
||||
- `oh` = **o**pen **h**ihat - Offener Hallo Hut
|
||||
|
||||
Probier verschiedene Sounds aus!
|
||||
|
||||
</Box>
|
||||
|
||||
Wir können den Charakter des Drum Sounds verändern, indem wir mit `bank` die Drum Machine auswählen:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh sd oh").bank("RolandTR909")`} hideHeader />
|
||||
|
||||
In diesem Beispiel ist `RolandTR909` der Name der Drum Machine, die eine prägende Rolle für House und Techno Musik spielte.
|
||||
|
||||
<Box>
|
||||
|
||||
Ändere `RolandTR909` in
|
||||
|
||||
- `AkaiLinn`
|
||||
- `RhythmAce`
|
||||
- `RolandTR808`
|
||||
- `RolandTR707`
|
||||
- `ViscoSpaceDrum`
|
||||
|
||||
Es gibt noch viel mehr, aber das sollte fürs Erste reichen..
|
||||
|
||||
🦥 Tipp für faule: Mach Doppel-Klick auf einen Namen um ihn zu markieren.
|
||||
Dann kannst du ihn mit `Strg`+`C` kopieren und mit `Strg`+`V` einfügen.
|
||||
|
||||
</Box>
|
||||
|
||||
## Sequenzen / Sequences
|
||||
|
||||
Im letzten Beispiel haben wir schon gesehen dass man mehrere Sounds hintereinander abspielen kann wenn man sie durch Leerzeichen trennt:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh sd hh")`} punchcard />
|
||||
|
||||
Beachte wie der aktuell gespielte Sound im Code markiert und auch darunter visualisiert wird.
|
||||
|
||||
<Box>
|
||||
|
||||
Versuch noch mehr Sounds hinzuzfügen!
|
||||
|
||||
</Box>
|
||||
|
||||
**Je länger die Sequence, desto schneller**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd bd hh bd rim bd hh bd")`} punchcard />
|
||||
|
||||
Der Inhalt einer Sequence wird in einen sogenannten Cycle (=Zyklus) zusammengequetscht.
|
||||
|
||||
**Tempo ändern mit `cpm`**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd bd hh bd rim bd hh bd").cpm(40)`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
cpm = **c**ycles per **m**inute = Cycles pro Minute
|
||||
|
||||
Das Tempo ist standardmäßig auf 60cpm eingestellt, also 1 Cycle pro Sekunde.
|
||||
|
||||
`cpm` ist angelehnt an `bpm` (=beats per minute).
|
||||
|
||||
</Box>
|
||||
|
||||
Wir werden später noch mehr Möglichkeiten kennen lernen das Tempo zu verändern.
|
||||
|
||||
**Pausen mit '~'**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh ~ rim")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Tilde tippen:
|
||||
|
||||
- Windows / Linux: `Alt Gr` + `~`
|
||||
- Mac: `option` + `N`
|
||||
|
||||
</Box>
|
||||
|
||||
**Unter-Sequenzen mit [Klammern]**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd [hh hh] rim [hh hh]")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Der Inhalt der Klammer wird ebenfalls zusammengequetscht!
|
||||
|
||||
Füge noch mehr Sounds in die Klammern ein!
|
||||
|
||||
</Box>
|
||||
|
||||
Genau wie bei der ganzen Sequence wird eine Unter-Sequence schneller je mehr drin ist.
|
||||
|
||||
**Multiplikation: Dinge schneller machen**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh*2 sd hh*3")`} punchcard />
|
||||
|
||||
**Multiplikation: Vieeeeeeel schneller**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh*16 sd hh*8")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Tonhöhe = sehr schneller Rhythmus
|
||||
|
||||
</Box>
|
||||
|
||||
**Multiplikation: Ganze Unter-Sequences schneller machen**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd [sd hh]*2")`} punchcard />
|
||||
|
||||
Bolero:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("sd sd*3 sd sd*3 sd sd sd sd*3 sd sd*3 sd*3 sd*3")
|
||||
.cpm(10)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Unter-Unter-Sequenzen mit [[Klammern]]**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd [[rim rim] hh]")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Du kannst so tief verschachteln wie du willst!
|
||||
|
||||
</Box>
|
||||
|
||||
**Parallele Sequenzen mit Komma**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh hh hh, bd casio")`} punchcard />
|
||||
|
||||
Du kannst so viele Kommas benutzen wie du möchtest:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh hh hh, bd bd, ~ casio")`} punchcard />
|
||||
|
||||
Kommas können auch in Unter-Sequenzen verwendet werden:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh hh hh, bd [bd,casio]")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Ist dir aufgefallen dass sich die letzten beiden Beispiele gleich anhören?
|
||||
|
||||
Es kommt öfter vor, dass man die gleiche Idee auf verschiedene Arten ausdrücken kann.
|
||||
|
||||
</Box>
|
||||
|
||||
**Mehrere Zeilen schreiben mit \` (backtick)**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound(\`bd*2, ~ cp,
|
||||
~ ~ ~ oh, hh*4,
|
||||
[~ casio]*2\`)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Ob man " oder \` benutzt ist nur eine Frage der Übersichtlichkeit.
|
||||
|
||||
</Box>
|
||||
|
||||
**Sample Nummer separat auswählen**
|
||||
|
||||
Benutzt man nur einen Sound mit unterschiedlichen Sample Nummer sieht das so aus:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("jazz:0 jazz:1 [jazz:4 jazz:2] jazz:3*2")`} punchcard />
|
||||
|
||||
Das gleiche kann man auch so schreiben:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 2] 3*2").sound("jazz")`} punchcard />
|
||||
|
||||
## Rückblick
|
||||
|
||||
Wir haben jetzt die Grundlagen der sogenannten Mini-Notation gelernt, der Rhythmus-Sprache von Tidal.
|
||||
|
||||
Das haben wir bisher gelernt:
|
||||
|
||||
| Concept | Syntax | Example |
|
||||
| --------------------- | ----------- | -------------------------------------------------------------------------------- |
|
||||
| Sequenz | Leerzeichen | <MiniRepl hideHeader client:visible tune={`sound("bd bd sd hh")`} /> |
|
||||
| Sound Nummer | :x | <MiniRepl hideHeader client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
|
||||
| Pausen | ~ | <MiniRepl hideHeader client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
|
||||
| Unter-Sequenzen | \[\] | <MiniRepl hideHeader client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
|
||||
| Unter-Unter-Sequenzen | \[\[\]\] | <MiniRepl hideHeader client:visible tune={`sound("bd [metal [jazz sd]]")`} /> |
|
||||
| Schneller | \* | <MiniRepl hideHeader client:visible tune={`sound("bd sd*2 cp*3")`} /> |
|
||||
| Parallel | , | <MiniRepl hideHeader client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
|
||||
|
||||
Die mit Apostrophen umgebene Mini-Notation benutzt man normalerweise in eine sogenannten Funktion.
|
||||
Die folgenden Funktionen haben wir bereits gesehen:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | -------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| sound | Spielt den Sound mit dem Namen | <MiniRepl hideHeader client:visible tune={`sound("bd sd")`} /> |
|
||||
| bank | Wählt die Soundbank / Drum Machine | <MiniRepl hideHeader client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
|
||||
| cpm | Tempo in **C**ycles **p**ro **M**inute | <MiniRepl hideHeader client:visible tune={`sound("bd sd").cpm(90)`} /> |
|
||||
| n | Sample Nummer | <MiniRepl hideHeader client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
|
||||
|
||||
## Beispiele
|
||||
|
||||
**Einfacher Rock Beat**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd sd, hh*4").bank("RolandTR505").cpm(100/2)`} punchcard />
|
||||
|
||||
**Klassischer House**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd*2, ~ cp, [~ hh]*2").bank("RolandTR909")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Ist die aufgefallen dass die letzten 2 Patterns extrem ähnlich sind?
|
||||
Bestimmte Drum Patterns werden oft genreübergreifend wiederverwendet.
|
||||
|
||||
</Box>
|
||||
|
||||
We Will Rock you
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd*2 cp").bank("RolandTR707").cpm(81/2)`} punchcard />
|
||||
|
||||
**Yellow Magic Orchestra - Firecracker**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("bd sd, ~ ~ ~ hh ~ hh ~ ~, ~ perc ~ perc:1*2")
|
||||
.bank("RolandCompurhythm1000")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Nachahmung eines 16 step sequencers**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
[~ ~ oh ~ ] [~ ~ ~ ~ ] [~ ~ ~ ~ ] [~ ~ ~ ~ ],
|
||||
[hh hh ~ ~ ] [hh ~ hh ~ ] [hh ~ hh ~ ] [hh ~ hh ~ ],
|
||||
[~ ~ ~ ~ ] [cp ~ ~ ~ ] [~ ~ ~ ~ ] [cp ~ ~ ~ ],
|
||||
[bd ~ ~ ~ ] [~ ~ ~ bd] [~ ~ bd ~ ] [~ ~ ~ bd]
|
||||
\`).cpm(90/4)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Noch eins**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
[~ ~ ~ ~ ] [~ ~ ~ ~ ] [~ ~ ~ ~ ] [~ ~ oh:1 ~ ],
|
||||
[hh hh hh hh] [hh hh hh hh] [hh hh hh hh] [hh hh ~ ~ ],
|
||||
[~ ~ ~ ~ ] [cp ~ ~ ~ ] [~ ~ ~ ~ ] [~ cp ~ ~ ],
|
||||
[bd bd ~ ~ ] [~ ~ bd ~ ] [bd bd ~ bd ] [~ ~ ~ ~ ]
|
||||
\`).bank("RolandTR808").cpm(88/4)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Nicht so typische Drums**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`s(\`jazz*2,
|
||||
insect [crow metal] ~ ~,
|
||||
~ space:4 ~ space:1,
|
||||
~ wind\`)
|
||||
.cpm(100/2)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
Jetzt haben wir eine grundlegende Ahnung davon wie man mit Strudel Beats baut!
|
||||
Im nächsten Kapitel werden wir ein paar [Töne spielen](/de/workshop/first-notes).
|
||||
|
||||
---
|
||||
|
||||
- vllt mal das wörtchen `sound` behandeln
|
||||
- was ist überhaupt casio??
|
||||
- Strg = ctrl on mac "+" ist verwirrend
|
||||
- cursor ist sehr undeutlich wenn nicht in emacs mode
|
||||
- wieviele zahlen gibt es
|
||||
74
website/src/pages/de/workshop/getting-started.mdx
Normal file
74
website/src/pages/de/workshop/getting-started.mdx
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
title: Intro
|
||||
layout: ../../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../../docs/MiniRepl';
|
||||
|
||||
# Willkommen
|
||||
|
||||
<img src="/icons/strudel_icon.png" className="w-32 animate-pulse md:float-right ml-8" />
|
||||
|
||||
Willkommen zum Strudel Workshop!
|
||||
Du hast den richtigen Ort gefunden wenn du lernen möchstest wie man mit Code Musik macht.
|
||||
|
||||
## Was ist Strudel
|
||||
|
||||
Mit Strudel kann man dynamische Musikstücke in Echtzeit schreiben.
|
||||
Es ist eine in JavaScript entwickelte Version von [Tidal Cycles](https://tidalcycles.org/) und wurde 2022 von Alex McLean und Felix Roos initiiert.
|
||||
Tidal Cycles, auch bekannt unter dem Namen Tidal, ist eine Computersprache für algorithmische Muster.
|
||||
Obwohl sie meistens für die Erzeugung von Musik eingesetzt wird, kann sie für jede Art von Tätigkeit eingesetzt werden,
|
||||
in der Muster eine Rolle spielen.
|
||||
|
||||
Du brauchst keine Erfahrung in JavaScript oder Tidal Cycles um mit Strudel Musik zu machen.
|
||||
Dieser interaktive Workshop leitet dich spielerisch durch die Grundlagen von Strudel.
|
||||
Der beste Ort um mit Strudel Musik zu machen ist das [Strudel REPL](https://strudel.tidalcycles.org/).
|
||||
|
||||
## Was kann man mit Strudel machen?
|
||||
|
||||
- Musik Live Coding: In Echtzeit mit Code Musik machen
|
||||
- Algorithmische Komposition: Schreibe Musik mithilfe Tidals einzigartiger Sprache für Muster
|
||||
- Lehren: Strudel eignet sich gut für Lehrende, da keine Installation nötig ist und die Sprache kein theoretisches Vorwissen erfordert.
|
||||
- Mit anderen Musik-Anwendungen kombinieren: Per MIDI oder OSC kann Strudel als flexibler Sequencer mit jedem Setup kombiniert werden
|
||||
|
||||
## Beispiel
|
||||
|
||||
Hier ist ein Beispiel wie Strudel klingen kann:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`stack(
|
||||
// drums
|
||||
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8")
|
||||
.speed(perlin.range(.8,.9)), // random sample speed variation
|
||||
// bassline
|
||||
"<a1 b1\*2 a1(3,8) e2>"
|
||||
.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
|
||||
.note() // wrap in "note"
|
||||
.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
|
||||
// chords
|
||||
"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand')
|
||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||
.add(perlin.range(0,.5)) // random pitch variation
|
||||
.note() // wrap in "note"
|
||||
.s('sawtooth') // waveform
|
||||
.gain(.16) // turn down
|
||||
.cutoff(500) // fixed cutoff
|
||||
.attack(1) // slowly fade in
|
||||
)
|
||||
.slow(3/2)`}
|
||||
/>
|
||||
|
||||
Mehr Beispiele gibt es [hier](/examples).
|
||||
|
||||
Du kannst auch im [Strudel REPL](https://strudel.tidalcycles.org/) auf `shuffle` klicken um ein zufälliges Beispiel zu hören.
|
||||
|
||||
## Workshop
|
||||
|
||||
Der beste Weg um Strudel zu lernen ist der nun folgende Workshop.
|
||||
Wenn du bereit bist, lass uns loslegen mit deinen [ersten Sounds](/de/workshop/first-sounds).
|
||||
3
website/src/pages/de/workshop/index.astro
Normal file
3
website/src/pages/de/workshop/index.astro
Normal file
@ -0,0 +1,3 @@
|
||||
<script is:inline>
|
||||
window.location.pathname = `/workshop/intro`;
|
||||
</script>
|
||||
183
website/src/pages/de/workshop/pattern-effects.mdx
Normal file
183
website/src/pages/de/workshop/pattern-effects.mdx
Normal file
@ -0,0 +1,183 @@
|
||||
---
|
||||
title: Pattern Effekte
|
||||
layout: ../../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# Pattern Effekte
|
||||
|
||||
Bis jetzt sind die meisten Funktionen die wir kennengelernt haben ähnlich wie Funktionen in anderen Musik Programmen: Sequencing von Sounds, Noten und Effekten.
|
||||
|
||||
In diesem Kapitel beschäftigen wir uns mit Funktionen die weniger herkömmlich oder auch enzigartig sind.
|
||||
|
||||
**rev = rückwärts abspielen**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 3] 2").sound("jazz").rev()`} />
|
||||
|
||||
**jux = einen stereo kanal modifizieren**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 3] 2").sound("jazz").jux(rev)`} />
|
||||
|
||||
So würde man das ohne jux schreiben:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(0),
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(1).rev()
|
||||
)`}
|
||||
/>
|
||||
|
||||
Lass uns visualisieren was hier passiert:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(0).color("cyan"),
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(1).color("magenta").rev()
|
||||
)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Schreibe `//` vor eine der beiden Zeilen im `stack`!
|
||||
|
||||
</Box>
|
||||
|
||||
**mehrere tempos**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("c2, eb3 g3 [bb3 c4]").sound("piano").slow("1,2,3")`} />
|
||||
|
||||
Das hat den gleichen Effekt wie:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1).color('cyan'),
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(2).color('magenta'),
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(3).color('yellow')
|
||||
)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Schreibe wieder `//` vor eine oder mehrere Zeilen im `stack`.
|
||||
|
||||
</Box>
|
||||
|
||||
**add = addieren**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c2 [eb3,g3]".add("<0 <1 -1>>"))
|
||||
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Addiert man eine Zahl zu einer Note, verhält sich diese wie eine Zahl.
|
||||
|
||||
z.B. c4 = 60, also ist c4 + 2 = 62
|
||||
|
||||
</Box>
|
||||
|
||||
Man kann auch mehrmals addieren:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c2 [eb3,g3]".add("<0 <1 -1>>").add("0,7"))
|
||||
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**add + scale**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<0 [2 4] <3 5> [~ <4 1>]>*2".add("<0 [0,2,4]>/4"))
|
||||
.scale("C5:minor").release(.5)
|
||||
.sound("gm_xylophone").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Alles zusammen**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("<0 [2 4] <3 5> [~ <4 1>]>*2".add("<0 [0,2,4]>/4"))
|
||||
.scale("C5:minor")
|
||||
.sound("gm_xylophone")
|
||||
.room(.4).delay(.125),
|
||||
note("c2 [eb3,g3]".add("<0 <1 -1>>"))
|
||||
.adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass")
|
||||
.room(.5),
|
||||
n("0 1 [2 3] 2").sound("jazz").jux(rev).slow(2)
|
||||
)`}
|
||||
/>
|
||||
|
||||
**ply**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh, bd rim").bank("RolandTR707").ply(2)`} punchcard />
|
||||
|
||||
das ist wie:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh*2, bd*2 rim*2").bank("RolandTR707")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Probier `ply` mit einem pattern zu automatisieren, z.b. `"<1 2 1 3>"`
|
||||
|
||||
</Box>
|
||||
|
||||
**off**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<0 [4 <3 2>] <2 3> [~ 1]>"
|
||||
.off(1/8, x=>x.add(4))
|
||||
//.off(1/4, x=>x.add(7))
|
||||
).scale("<C5:minor Db5:mixolydian>/4")
|
||||
.s("triangle").room(.5).ds(".1:0").delay(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
In der notation `x=>x.`, das `x` ist das Pattern das wir bearbeiten.
|
||||
|
||||
</Box>
|
||||
|
||||
`off` ist auch nützlich für sounds:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`s("bd sd,[~ hh]*2").bank("CasioRZ1")
|
||||
.off(1/8, x=>x.speed(1.5).gain(.25))`}
|
||||
/>
|
||||
|
||||
| name | description | example |
|
||||
| ---- | --------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| rev | rückwärts | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | ein stereo-kanal modifizieren | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | addiert zahlen oder noten | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | multipliziert jedes element x mal | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | verzögert eine modifizierte kopie | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
68
website/src/pages/de/workshop/recap.mdx
Normal file
68
website/src/pages/de/workshop/recap.mdx
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Recap
|
||||
layout: ../../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../../docs/MiniRepl';
|
||||
|
||||
# Workshop Rückblick
|
||||
|
||||
Diese Seite ist eine Auflistung aller im Workshop enthaltenen Funktionen.
|
||||
|
||||
## Mini Notation
|
||||
|
||||
| Concept | Syntax | Example |
|
||||
| --------------------- | -------- | -------------------------------------------------------------------------------- |
|
||||
| Sequence | space | <MiniRepl hideHeader client:visible tune={`sound("bd bd sn hh")`} /> |
|
||||
| Sample Nummer | :x | <MiniRepl hideHeader client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
|
||||
| Pausen | ~ | <MiniRepl hideHeader client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
|
||||
| Unter-Sequences | \[\] | <MiniRepl hideHeader client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
|
||||
| Unter-Unter-Sequences | \[\[\]\] | <MiniRepl hideHeader client:visible tune={`sound("bd [metal [jazz sn]]")`} /> |
|
||||
| Schneller | \* | <MiniRepl hideHeader client:visible tune={`sound("bd sn*2 cp*3")`} /> |
|
||||
| Slow down | \/ | <MiniRepl hideHeader client:visible tune={`note("[c a f e]/2")`} /> |
|
||||
| Parallel | , | <MiniRepl hideHeader client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
|
||||
| Alternieren | \<\> | <MiniRepl hideHeader client:visible tune={`note("c <e g>")`} /> |
|
||||
| Verlängern | @ | <MiniRepl hideHeader client:visible tune={`note("c@3 e")`} /> |
|
||||
| Wiederholen | ! | <MiniRepl hideHeader client:visible tune={`note("c!3 e")`} /> |
|
||||
|
||||
## Sounds
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | -------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| sound | spielt den sound mit namen | <MiniRepl hideHeader client:visible tune={`sound("bd sd")`} /> |
|
||||
| bank | wählt die soundbank | <MiniRepl hideHeader client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
|
||||
| n | wählt sample mit nummer | <MiniRepl hideHeader client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
|
||||
|
||||
## Notes
|
||||
|
||||
| Name | Description | Example |
|
||||
| --------- | ---------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| note | wählt note per zahl oder buchstabe | <MiniRepl hideHeader client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| n + scale | wählt note n in skala | <MiniRepl hideHeader client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| stack | spielt mehrere patterns parallel | <MiniRepl hideHeader client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
|
||||
|
||||
## Audio Effekte
|
||||
|
||||
| name | example |
|
||||
| ----- | -------------------------------------------------------------------------------------------------- |
|
||||
| lpf | <MiniRepl hideHeader client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
|
||||
| vowel | <MiniRepl hideHeader client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
|
||||
| gain | <MiniRepl hideHeader client:visible tune={`s("hh*8").gain("[.25 1]*2")`} /> |
|
||||
| delay | <MiniRepl hideHeader client:visible tune={`s("bd rim").delay(.5)`} /> |
|
||||
| room | <MiniRepl hideHeader client:visible tune={`s("bd rim").room(.5)`} /> |
|
||||
| pan | <MiniRepl hideHeader client:visible tune={`s("bd rim").pan("0 1")`} /> |
|
||||
| speed | <MiniRepl hideHeader client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
|
||||
| range | <MiniRepl hideHeader client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
|
||||
|
||||
## Pattern Effects
|
||||
|
||||
| name | description | example |
|
||||
| ---- | --------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| cpm | tempo in cycles pro minute | <MiniRepl hideHeader client:visible tune={`sound("bd sd").cpm(90)`} /> |
|
||||
| fast | schneller | <MiniRepl hideHeader client:visible tune={`sound("bd sd").fast(2)`} /> |
|
||||
| slow | langsamer | <MiniRepl hideHeader client:visible tune={`sound("bd sd").slow(2)`} /> |
|
||||
| rev | rückwärts | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | ein stereo-kanal modifizieren | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | addiert zahlen oder noten | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | jedes element schneller machen | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | verzögert eine modifizierte kopie | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
@ -6,23 +6,24 @@ layout: ../../layouts/MainLayout.astro
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Functional JavaScript API
|
||||
# Pattern Functions
|
||||
|
||||
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.
|
||||
Let's learn all about functions to create and modify patterns.
|
||||
At the core of Strudel, everything is made of functions.
|
||||
|
||||
For example, this Pattern in Mini Notation:
|
||||
For example, everything you can do with the Mini-Notation can also be done with a function.
|
||||
This Pattern in Mini Notation:
|
||||
|
||||
<MiniRepl client:only="react" tune={`note("c3 eb3 g3")`} />
|
||||
|
||||
is equivalent to this Pattern without Mini Notation:
|
||||
|
||||
<MiniRepl client:only="react" tune={`note(seq(c3, eb3, g3))`} />
|
||||
<MiniRepl client:only="react" 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.
|
||||
Which representation to use is a matter of context. As a rule of thumb, functions
|
||||
are better suited in a larger context, while mini notation is more practical for individiual rhythms.
|
||||
|
||||
## Limits of Mini Notation
|
||||
|
||||
@ -46,10 +47,10 @@ You can freely mix JS patterns, mini patterns and values! For example, this patt
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`cat(
|
||||
stack(g3,b3,e4),
|
||||
stack(a3,c3,e4),
|
||||
stack(b3,d3,fs4),
|
||||
stack(b3,e4,g4)
|
||||
stack("g3","b3","e4"),
|
||||
stack("a3","c3","e4"),
|
||||
stack("b3","d3","fs4"),
|
||||
stack("b3","e4","g4")
|
||||
).note()`}
|
||||
/>
|
||||
|
||||
@ -72,4 +73,4 @@ You can freely mix JS patterns, mini patterns and values! For example, this patt
|
||||
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.
|
||||
|
||||
What [Pattern Constructors](/learn/factories) does Strudel offer?
|
||||
Next, let's look at how you can [create patterns](/learn/factories)
|
||||
|
||||
@ -6,31 +6,31 @@ layout: ../../layouts/MainLayout.astro
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Strudel Code
|
||||
# Coding Syntax
|
||||
|
||||
Now that we have played some notes using different sounds, let's take a step back and look how we actually achieved this using _code_.
|
||||
Let's take a step back and understand how the syntax in Strudel works.
|
||||
|
||||
Let's look at this simple example again. What do we notice?
|
||||
Take a look at this simple example:
|
||||
|
||||
<MiniRepl client:idle tune={`freq("220 275 330 440").s("sine")`} />
|
||||
<MiniRepl client:idle tune={`note("c a f e").s("piano")`} />
|
||||
|
||||
- We have a word `freq` which is followed by some brackets `()` with some words/letters/numbers inside, surrounded by quotes `"220 275 330 440"` (corresponding to the pitches a3, c#4, e4, a4).
|
||||
- Then we have a dot `.` followed by another similar piece of code `s("sine")`.
|
||||
- We can also see these texts are _highlighted_ using colours: word `freq` is purple, the brackets `()` are grey, and the content inside the `""` are green.
|
||||
- We have a word `note` which is followed by some brackets `()` with some words/letters/numbers inside, surrounded by quotes `"c a f e"`
|
||||
- Then we have a dot `.` followed by another similar piece of code `s("piano")`.
|
||||
- We can also see these texts are _highlighted_ using colours: word `note` is purple, the brackets `()` are grey, and the content inside the `""` are green. (The colors could be different if you've changed the default theme)
|
||||
|
||||
What happens if we try to 'break' this pattern in different ways?
|
||||
|
||||
<MiniRepl client:idle tune={`freq(220 275 330 440).s(sine)`} />
|
||||
<MiniRepl client:idle tune={`note(c a f e).s(piano)`} />
|
||||
|
||||
<MiniRepl client:idle tune={`freq("220 275 330 440")s("sine")`} />
|
||||
<MiniRepl client:idle tune={`note("c a f e")s("piano")`} />
|
||||
|
||||
<MiniRepl client:idle tune={`freq["220 275 330 440"].s{"sine"}`} />
|
||||
<MiniRepl client:idle tune={`note["c a f e"].s{"piano"}`} />
|
||||
|
||||
Ok, none of these seem to work...
|
||||
|
||||
<MiniRepl client:idle tune={`s("sine").freq("220 275 330 440")`} />
|
||||
<MiniRepl client:idle tune={`s("piano").note("c a f e")`} />
|
||||
|
||||
This one does work, but now we can't hear the four different events and frequencies anymore.
|
||||
This one does work, but now we only hear the first note...
|
||||
|
||||
So what is going on here?
|
||||
|
||||
@ -72,14 +72,12 @@ Those are just a convention to define some information about the music. We will
|
||||
|
||||
# Strings
|
||||
|
||||
Ok, so what about the content inside the quotes (e.g. `"a3 c#4 e4 a4"`)?
|
||||
Ok, so what about the content inside the quotes (e.g. `"c a f e"`)?
|
||||
In JavaScript, as in most programming languages, this content is referred to as being a [_string_](<https://en.wikipedia.org/wiki/String_(computer_science)>).
|
||||
A string is simply a sequence of individual characters.
|
||||
In TidalCycles, double quoted strings are used to write _patterns_ using the mini-notation, and you may hear the phrase _pattern string_ from time to time.
|
||||
If you want to create a regular string and not a pattern, you can use single quotes, e.g. `'C minor'` will not be parsed as Mini Notation.
|
||||
|
||||
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](/learn/mini-notation)...
|
||||
The good news is, that this covers most of the JavaScript syntax needed for Strudel!
|
||||
|
||||
<br />
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Pattern Constructors
|
||||
title: Creating Patterns
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Pattern Constructors
|
||||
# Creating Patterns
|
||||
|
||||
The following functions will return a pattern.
|
||||
These are the equivalents used by the Mini Notation:
|
||||
|
||||
@ -8,11 +8,17 @@ import { JsDoc } from '../../docs/JsDoc';
|
||||
|
||||
# Mini-notation
|
||||
|
||||
Similar to [Haskell Tidal Cycles](https://tidalcycles.org/docs/), Strudel has an "embedded mini-notation" (also called a [domain-specific language, or DSL](https://en.wikipedia.org/wiki/Domain-specific_language)) that is designed for writing rhythmic patterns using little amounts of text.
|
||||
If you've seen any Tidal code before, you may have noticed the mini-notation and wondered what it's all about.
|
||||
It's one of the main features of Tidal, and although it might look like a strange way to notate music and other patterns, you will soon see how powerful it can be.
|
||||
Just like [Tidal Cycles](https://tidalcycles.org/), Strudel uses a so called "Mini-Notation", which is a custom language that is designed for writing rhythmic patterns using little amounts of text.
|
||||
|
||||
Before diving deeper into the details, here is a flavour of how the mini-notation looks like:
|
||||
## Note
|
||||
|
||||
This page just explains the entirety of the Mini-Notation syntax.
|
||||
If you are just getting started with Strudel, you can learn the basics of the Mini-Notation in a more practical manner in the [workshop](http://localhost:3000/workshop/first-sounds).
|
||||
After that, you can come back here if you want to understand every little detail.
|
||||
|
||||
## Example
|
||||
|
||||
Before diving deeper into the details, here is a flavour of how the Mini-Notation looks like:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
|
||||
335
website/src/pages/workshop/first-effects.mdx
Normal file
335
website/src/pages/workshop/first-effects.mdx
Normal file
@ -0,0 +1,335 @@
|
||||
---
|
||||
title: First Effects
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# First Effects
|
||||
|
||||
import Box from '@components/Box.astro';
|
||||
|
||||
We have sounds, we have notes, now let's look at effects!
|
||||
|
||||
## Some basic effects
|
||||
|
||||
**low-pass filter**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth").lpf(800)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
lpf = **l**ow **p**ass **f**ilter
|
||||
|
||||
- Change lpf to 200. Notice how it gets muffled. Think of it as standing in front of the club with the door closed 🚪.
|
||||
- Now let's open the door... change it to 5000. Notice how it gets brighter ✨🪩
|
||||
|
||||
</Box>
|
||||
|
||||
**pattern the filter**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth").lpf("200 1000")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
- Try adding more values
|
||||
- Notice how the pattern in lpf does not change the overall rhythm
|
||||
|
||||
We will learn how to automate with waves later...
|
||||
|
||||
</Box>
|
||||
|
||||
**vowel**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
|
||||
.sound("sawtooth").vowel("<a e i o>/2")`}
|
||||
/>
|
||||
|
||||
**gain**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
sound("hh*8").gain("[.25 1]*2"),
|
||||
sound("bd*2,~ sd:1")
|
||||
) `}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Rhythm is all about dynamics!
|
||||
|
||||
- Remove `.gain(...)` and notice how flat it sounds.
|
||||
- Bring it back by undoing (ctrl+z)
|
||||
|
||||
</Box>
|
||||
|
||||
**stacks within stacks**
|
||||
|
||||
Let's combine all of the above into a little tune:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
stack(
|
||||
sound("hh*8").gain("[.25 1]*2"),
|
||||
sound("bd*2,~ sd:1")
|
||||
),
|
||||
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth").lpf("200 1000"),
|
||||
note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
|
||||
.sound("sawtooth").vowel("<a e i o>/2")
|
||||
) `}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try to identify the individual parts of the stacks, pay attention to where the commas are.
|
||||
The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together, separated by comma.
|
||||
|
||||
</Box>
|
||||
|
||||
**shape the sound with an adsr envelope**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<c3 bb2 f3 eb3>")
|
||||
.sound("sawtooth").lpf(600)
|
||||
.attack(.1)
|
||||
.decay(.1)
|
||||
.sustain(.25)
|
||||
.release(.2)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try to find out what the numbers do.. Compare the following
|
||||
|
||||
- attack: `.5` vs `0`
|
||||
- decay: `.5` vs `0`
|
||||
- sustain: `1` vs `.25` vs `0`
|
||||
- release: `0` vs `.5` vs `1`
|
||||
|
||||
Can you guess what they do?
|
||||
|
||||
</Box>
|
||||
|
||||
<QA q="Click to see solution" client:visible>
|
||||
|
||||
- attack: time it takes to fade in
|
||||
- decay: time it takes to fade to sustain
|
||||
- sustain: level after decay
|
||||
- release: time it takes to fade out after note is finished
|
||||
|
||||

|
||||
|
||||
</QA>
|
||||
|
||||
**adsr short notation**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<c3 bb2 f3 eb3>")
|
||||
.sound("sawtooth").lpf(600)
|
||||
.adsr(".1:.1:.5:.2")
|
||||
`}
|
||||
/>
|
||||
|
||||
**delay**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
|
||||
.sound("gm_electric_guitar_muted"),
|
||||
sound("<bd rim>").bank("RolandTR707")
|
||||
).delay(".5")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try some `delay` values between 0 and 1. Btw, `.5` is short for `0.5`
|
||||
|
||||
What happens if you use `.delay(".8:.125")` ? Can you guess what the second number does?
|
||||
|
||||
What happens if you use `.delay(".8:.06:.8")` ? Can you guess what the third number does?
|
||||
|
||||
</Box>
|
||||
|
||||
<QA q="Lösung anzeigen" client:visible>
|
||||
|
||||
`delay("a:b:c")`:
|
||||
|
||||
- a: delay volume
|
||||
- b: delay time
|
||||
- c: feedback (smaller number = quicker fade)
|
||||
|
||||
</QA>
|
||||
|
||||
**room aka reverb**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try different values!
|
||||
|
||||
Add a delay too!
|
||||
|
||||
</Box>
|
||||
|
||||
**little dub tune**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
|
||||
.sound("gm_electric_guitar_muted").delay(.5),
|
||||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2).gain(.5)
|
||||
)`}
|
||||
/>
|
||||
|
||||
Let's add a bass to make this complete:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
|
||||
.sound("gm_electric_guitar_muted").delay(.5),
|
||||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
.room(2).gain(.4),
|
||||
n("<0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~>*2")
|
||||
.scale("D2:minor")
|
||||
.sound("sawtooth,triangle").lpf(800)
|
||||
)`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try adding `.hush()` at the end of one of the patterns in the stack...
|
||||
|
||||
</Box>
|
||||
|
||||
**pan**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("numbers:1 numbers:2 numbers:3 numbers:4")
|
||||
.pan("0 0.3 .6 1")
|
||||
.slow(2)`}
|
||||
/>
|
||||
|
||||
**speed**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd rim").speed("<1 2 -1 -2>").room(.2)`} />
|
||||
|
||||
**fast and slow**
|
||||
|
||||
We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd*2,~ rim").slow(2)`} />
|
||||
|
||||
<Box>
|
||||
|
||||
Change the `slow` value. Try replacing it with `fast`.
|
||||
|
||||
What happens if you use a pattern like `.fast("<1 [2 4]>")`?
|
||||
|
||||
</Box>
|
||||
|
||||
By the way, inside Mini-Notation, `fast` is `*` and `slow` is `/`.
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("[bd*2,~ rim]*<1 [2 4]>")`} />
|
||||
|
||||
## automation with signals
|
||||
|
||||
Instead of changing values stepwise, we can also control them with signals:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh*16").gain(sine)`} punchcard punchcardLabels={false} />
|
||||
|
||||
<Box>
|
||||
|
||||
The basic waveforms for signals are `sine`, `saw`, `square`, `tri` 🌊
|
||||
|
||||
Try also random signals `rand` and `perlin`!
|
||||
|
||||
The gain is visualized as transparency in the pianoroll.
|
||||
|
||||
</Box>
|
||||
|
||||
**setting a range**
|
||||
|
||||
By default, waves oscillate between 0 to 1. We can change that with `range`:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh*8").lpf(saw.range(500, 2000))`} />
|
||||
|
||||
<Box>
|
||||
|
||||
What happens if you flip the range values?
|
||||
|
||||
</Box>
|
||||
|
||||
We can change the automation speed with slow / fast:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("sawtooth")
|
||||
.lpf(sine.range(100, 2000).slow(8))`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
The whole automation will now take 8 cycles to repeat.
|
||||
|
||||
</Box>
|
||||
|
||||
## Recap
|
||||
|
||||
| name | example |
|
||||
| ----- | -------------------------------------------------------------------------------------------------- |
|
||||
| lpf | <MiniRepl hideHeader client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
|
||||
| vowel | <MiniRepl hideHeader client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
|
||||
| gain | <MiniRepl hideHeader client:visible tune={`s("hh*8").gain("[.25 1]*2")`} /> |
|
||||
| delay | <MiniRepl hideHeader client:visible tune={`s("bd rim").delay(.5)`} /> |
|
||||
| room | <MiniRepl hideHeader client:visible tune={`s("bd rim").room(.5)`} /> |
|
||||
| pan | <MiniRepl hideHeader client:visible tune={`s("bd rim").pan("0 1")`} /> |
|
||||
| speed | <MiniRepl hideHeader client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
|
||||
| range | <MiniRepl hideHeader client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
|
||||
|
||||
Let us now take a look at some of Tidal's typical [pattern effects](/de/workshop/pattern-effects).
|
||||
390
website/src/pages/workshop/first-notes.mdx
Normal file
390
website/src/pages/workshop/first-notes.mdx
Normal file
@ -0,0 +1,390 @@
|
||||
---
|
||||
title: First Notes
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import { midi2note } from '@strudel.cycles/core/';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# First Notes
|
||||
|
||||
Let's look at how we can play notes
|
||||
|
||||
## numbers and notes
|
||||
|
||||
**play notes with numbers**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("48 52 55 59").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(
|
||||
Array(49)
|
||||
.fill()
|
||||
.map((_, i) => [midi2note(i + 36), i + 36]),
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try out different numbers!
|
||||
|
||||
Try decimal numbers, like 55.5
|
||||
|
||||
</Box>
|
||||
|
||||
**play notes with letters**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c e g b").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(['c3', 'd3', 'e3', 'f3', 'g3', 'a3', 'b3'].map((n) => [n, n.split('')[0]]))}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try out different letters (a - g).
|
||||
|
||||
Can you find melodies that are actual words? Hint: ☕ 😉 ⚪
|
||||
|
||||
</Box>
|
||||
|
||||
**add flats or sharps to play the black keys**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("db eb gb ab bb").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(
|
||||
['db3', 'eb3', 'gb3', 'ab3', 'bb3'].map((n) => [n, n.split('').slice(0, 2).join('')]),
|
||||
)}
|
||||
/>
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c# d# f# g# a#").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(
|
||||
['c#3', 'd#3', 'f#3', 'g#3', 'a#3'].map((n) => [n, n.split('').slice(0, 2).join('')]),
|
||||
)}
|
||||
/>
|
||||
|
||||
**play notes with letters in different octaves**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c2 e3 g4 b5").sound("piano")`}
|
||||
claviature
|
||||
claviatureLabels={Object.fromEntries(['c1', 'c2', 'c3', 'c4', 'c5'].map((n) => [n, n]))}
|
||||
claviatureLabels={Object.fromEntries(
|
||||
Array(49)
|
||||
.fill()
|
||||
.map((_, i) => [midi2note(i + 36), midi2note(i + 36)]),
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try out different octaves (1-8)
|
||||
|
||||
</Box>
|
||||
|
||||
If you are not comfortable with the note letter system, it should be easier to use numbers instead.
|
||||
Most of the examples below will use numbers for that reason.
|
||||
We will also look at ways to make it easier to play the right notes later.
|
||||
|
||||
## changing the sound
|
||||
|
||||
Just like with unpitched sounds, we can change the sound of our notes with `sound`:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("36 43, 52 59 62 64").sound("piano")`} />
|
||||
|
||||
{/* c2 g2, e3 b3 d4 e4 */}
|
||||
|
||||
<Box>
|
||||
|
||||
Try out different sounds:
|
||||
|
||||
- gm_electric_guitar_muted
|
||||
- gm_acoustic_bass
|
||||
- gm_voice_oohs
|
||||
- gm_blown_bottle
|
||||
- sawtooth
|
||||
- square
|
||||
- triangle
|
||||
- how about bd, sd or hh?
|
||||
- remove `.sound('...')` completely
|
||||
|
||||
</Box>
|
||||
|
||||
**switch between sounds**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("48 67 63 [62, 58]")
|
||||
.sound("piano gm_electric_guitar_muted")`}
|
||||
/>
|
||||
|
||||
**stack multiple sounds**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("48 67 63 [62, 58]")
|
||||
.sound("piano, gm_electric_guitar_muted")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
The `note` and `sound` patterns are combined!
|
||||
|
||||
We will see more ways to combine patterns later..
|
||||
|
||||
</Box>
|
||||
|
||||
## Longer Sequences
|
||||
|
||||
**Divide sequences with `/` to slow them down**
|
||||
|
||||
{/* [c2 bb1 f2 eb2] */}
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("[36 34 41 39]/4").sound("gm_acoustic_bass")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
The `/4` plays the sequence in brackets over 4 cycles (=4s).
|
||||
|
||||
So each of the 4 notes is 1s long.
|
||||
|
||||
Try adding more notes inside the brackets and notice how it gets faster.
|
||||
|
||||
</Box>
|
||||
|
||||
Because it is so common to just play one thing per cycle, you can..
|
||||
|
||||
**Play one per cycle with \< \>**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("<36 34 41 39>").sound("gm_acoustic_bass")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Try adding more notes inside the brackets and notice how it does **not** get faster.
|
||||
|
||||
</Box>
|
||||
|
||||
**Play one sequence per cycle**
|
||||
|
||||
{/* <[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2 */}
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[36 48]*4 [34 46]*4 [41 53]*4 [39 51]*4>/2")
|
||||
.sound("gm_acoustic_bass")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Alternate between multiple things**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("60 <63 62 65 63>")
|
||||
.sound("gm_xylophone")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
This is also useful for unpitched sounds:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("bd*2, ~ <sd cp>, [~ hh]*2")
|
||||
.bank("RolandTR909")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
## Scales
|
||||
|
||||
Finding the right notes can be difficult.. Scales are here to help:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("0 2 4 <[6,8] [7,9]>")
|
||||
.scale("C:minor").sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try out different numbers. Any number should sound good!
|
||||
|
||||
Try out different scales:
|
||||
|
||||
- C:major
|
||||
- A2:minor
|
||||
- D:dorian
|
||||
- G:mixolydian
|
||||
- A2:minor:pentatonic
|
||||
- F:major:pentatonic
|
||||
|
||||
</Box>
|
||||
|
||||
**automate scales**
|
||||
|
||||
Just like anything, we can automate the scale with a pattern:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<0 -3>, 2 4 <[6,8] [7,9]>")
|
||||
.scale("<C:major D:mixolydian>/4")
|
||||
.sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
If you have no idea what these scale mean, don't worry.
|
||||
These are just labels for different sets of notes that go well together.
|
||||
|
||||
Take your time and you'll find scales you like!
|
||||
|
||||
</Box>
|
||||
|
||||
## Repeat & Elongate
|
||||
|
||||
**Elongate with @**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("c@3 eb").sound("gm_acoustic_bass")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Not using `@` is like using `@1`. In the above example, c is 3 units long and eb is 1 unit long.
|
||||
|
||||
Try changing that number!
|
||||
|
||||
</Box>
|
||||
|
||||
**Elongate within sub-sequences**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<[4@2 4] [5@2 5] [6@2 6] [5@2 5]>*2")
|
||||
.scale("<C2:mixolydian F2:mixolydian>/4")
|
||||
.sound("gm_acoustic_bass")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
This groove is called a `shuffle`.
|
||||
Each beat has two notes, where the first is twice as long as the second.
|
||||
This is also sometimes called triplet swing. You'll often find it in blues and jazz.
|
||||
|
||||
</Box>
|
||||
|
||||
**Replicate**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("c!2 [eb,<g a bb a>]").sound("piano")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Try switching between `!`, `*` and `@`
|
||||
|
||||
What's the difference?
|
||||
|
||||
</Box>
|
||||
|
||||
## Recap
|
||||
|
||||
Let's recap what we've learned in this chapter:
|
||||
|
||||
| Concept | Syntax | Example |
|
||||
| --------- | ------ | ------------------------------------------------------------------- |
|
||||
| Slow down | \/ | <MiniRepl hideHeader client:visible tune={`note("[c a f e]/2")`} /> |
|
||||
| Alternate | \<\> | <MiniRepl hideHeader client:visible tune={`note("c <e g>")`} /> |
|
||||
| Elongate | @ | <MiniRepl hideHeader client:visible tune={`note("c@3 e")`} /> |
|
||||
| Replicate | ! | <MiniRepl hideHeader client:visible tune={`note("c!3 e")`} /> |
|
||||
|
||||
New functions:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | ----------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| note | set pitch as number or letter | <MiniRepl hideHeader client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| scale | interpret `n` as scale degree | <MiniRepl hideHeader client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| stack | play patterns in parallel (read on) | <MiniRepl hideHeader client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
|
||||
|
||||
## Examples
|
||||
|
||||
**Classy Bassline**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("gm_synth_bass_1")
|
||||
.lpf(800) // <-- we'll learn about this soon`}
|
||||
/>
|
||||
|
||||
**Classy Melody**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n(\`<
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
[~ 0] 1 [0 1] [~ 1]
|
||||
[~ 0] 3 [0 3] [~ 3]
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
>*2\`).scale("C4:minor")
|
||||
.sound("gm_synth_strings_1")`}
|
||||
/>
|
||||
|
||||
**Classy Drums**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("bd*2, ~ <sd cp>, [~ hh]*2")
|
||||
.bank("RolandTR909")`}
|
||||
/>
|
||||
|
||||
**If there just was a way to play all the above at the same time.......**
|
||||
|
||||
<Box>
|
||||
|
||||
It's called `stack` 😙
|
||||
|
||||
</Box>
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
|
||||
.sound("gm_synth_bass_1").lpf(800),
|
||||
n(\`<
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
[~ 0] 1 [0 1] [~ 1]
|
||||
[~ 0] 3 [0 3] [~ 3]
|
||||
[~ 0] 2 [0 2] [~ 2]
|
||||
>*2\`).scale("C4:minor")
|
||||
.sound("gm_synth_strings_1"),
|
||||
sound("bd*2, ~ <sd cp>, [~ hh]*2")
|
||||
.bank("RolandTR909")
|
||||
)`}
|
||||
/>
|
||||
|
||||
This is starting to sound like actual music! We have sounds, we have notes, now the last piece of the puzzle is missing: [effects](/workshop/first-effects)
|
||||
330
website/src/pages/workshop/first-sounds.mdx
Normal file
330
website/src/pages/workshop/first-sounds.mdx
Normal file
@ -0,0 +1,330 @@
|
||||
---
|
||||
title: First Sounds
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# First Sounds
|
||||
|
||||
This is the first chapter of the Strudel Workshop, nice to have you on board!
|
||||
|
||||
## Code Fields
|
||||
|
||||
The workshop is full of interactive code fields. Let's learn how to use those. Here is one:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("casio")`} dirt />
|
||||
|
||||
<Box>
|
||||
|
||||
1. ⬆️ click into the text field above ⬆️
|
||||
2. press `ctrl`+`enter` to play
|
||||
3. change `casio` to `metal`
|
||||
4. press `ctrl`+`enter` to update
|
||||
5. press `ctrl`+`.` to stop
|
||||
|
||||
</Box>
|
||||
|
||||
Congratulations, you are now live coding!
|
||||
|
||||
## Sounds
|
||||
|
||||
We have just played a sound with `sound` like this:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("casio")`} hideHeader />
|
||||
|
||||
<Box>
|
||||
|
||||
`casio` is one of many standard sounds.
|
||||
|
||||
Try out a few other sounds:
|
||||
|
||||
```
|
||||
insect wind jazz metal east crow casio space numbers
|
||||
```
|
||||
|
||||
You might hear a little pause while the sound is loading
|
||||
|
||||
</Box>
|
||||
|
||||
**Change Sample Number with :**
|
||||
|
||||
One Sound can contain multiple samples (audio files).
|
||||
|
||||
You can select the sample by appending `:` followed by a number to the name:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("casio:1")`} hideHeader />
|
||||
|
||||
<Box>
|
||||
|
||||
Try different sound / sample number combinations.
|
||||
|
||||
Not adding a number is like doing `:0`
|
||||
|
||||
</Box>
|
||||
|
||||
Now you know how to use different sounds.
|
||||
For now we'll stick to this little selection of sounds, but we'll find out how to load your own sounds later.
|
||||
|
||||
## Drum Sounds
|
||||
|
||||
By default, Strudel comes with a wide selection of drum sounds:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh sd oh")`} hideHeader />
|
||||
|
||||
<Box>
|
||||
|
||||
These letter combinations stand for different parts of a drum set:
|
||||
|
||||
- `bd` = **b**ass **d**rum
|
||||
- `sd` = **s**nare **d**rum
|
||||
- `sd` = **sd**are
|
||||
- `rim` = **rim**shot
|
||||
- `hh` = **h**i**h**at
|
||||
- `oh` = **o**pen **h**ihat
|
||||
|
||||
Try out different drum sounds!
|
||||
|
||||
</Box>
|
||||
|
||||
To change the sound character of our drums, we can use `bank` to change the drum machine:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh sd oh").bank("RolandTR909")`} hideHeader />
|
||||
|
||||
In this example `RolandTR909` is the name of the drum machine that we're using.
|
||||
It is a famous drum machine for house and techno beats.
|
||||
|
||||
<Box>
|
||||
|
||||
Try changing `RolandTR909` to one of
|
||||
|
||||
- `AkaiLinn`
|
||||
- `RhythmAce`
|
||||
- `RolandTR808`
|
||||
- `RolandTR707`
|
||||
- `ViscoSpaceDrum`
|
||||
|
||||
There are a lot more, but let's keep it simple for now
|
||||
|
||||
🦥 Pro-Tip: Mark a name via double click. Then just copy and paste!
|
||||
|
||||
</Box>
|
||||
|
||||
## Sequences
|
||||
|
||||
In the last example, we already saw that you can play multiple sounds in a sequence by separating them with a space:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh sd hh")`} punchcard />
|
||||
|
||||
Notice how the currently playing sound is highlighted in the code and also visualized below.
|
||||
|
||||
<Box>
|
||||
|
||||
Try adding more sounds to the sequence!
|
||||
|
||||
</Box>
|
||||
|
||||
**The longer the sequence, the faster it runs**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd bd hh bd rim bd hh bd")`} punchcard />
|
||||
|
||||
The content of a sequence will be squished into what's called a cycle.
|
||||
|
||||
**One way to change the tempo is using `cpm`**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd bd hh bd rim bd hh bd").cpm(40)`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
cpm = cycles per minute
|
||||
|
||||
By default, the tempo is 60 cycles per minute = 1 cycle per second.
|
||||
|
||||
</Box>
|
||||
|
||||
We will look at other ways to change the tempo later!
|
||||
|
||||
**Add a rests in a sequence with '~'**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh ~ rim")`} punchcard />
|
||||
|
||||
**Sub-Sequences with [brackets]**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd [hh hh] sd [hh bd]")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Try adding more sounds inside a bracket!
|
||||
|
||||
</Box>
|
||||
|
||||
Similar to the whole sequence, the content of a sub-sequence will be squished to the its own length.
|
||||
|
||||
**Multiplication: Speed things up**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh*2 rim hh*3")`} punchcard />
|
||||
|
||||
**Multiplication: Speed up sequences**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd [hh rim]*2")`} punchcard />
|
||||
|
||||
**Multiplication: Speeeeeeeeed things up**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd hh*16 rim hh*8")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Pitch = really fast rhythm
|
||||
|
||||
</Box>
|
||||
|
||||
**Sub-Sub-Sequences with [[brackets]]**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd [[rim rim] hh]")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
You can go as deep as you want!
|
||||
|
||||
</Box>
|
||||
|
||||
**Play sequences in parallel with comma**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh hh hh, bd casio")`} punchcard />
|
||||
|
||||
You can use as many commas as you want:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh hh hh, bd bd, ~ casio")`} punchcard />
|
||||
|
||||
Commas can also be used inside sub-sequences:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh hh hh, bd [bd,casio]")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Notice how the 2 above are the same?
|
||||
|
||||
It is quite common that there are many ways to express the same idea.
|
||||
|
||||
</Box>
|
||||
|
||||
**Multiple Lines with backticks**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound(\`bd*2, ~ cp,
|
||||
~ ~ ~ oh, hh*4,
|
||||
[~ casio]*2\`)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**selecting sample numbers separately**
|
||||
|
||||
Instead of using ":", we can also use the `n` function to select sample numbers:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 2] 3*2").sound("jazz")`} punchcard />
|
||||
|
||||
This is shorter and more readable than:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("jazz:0 jazz:1 [jazz:4 jazz:2] jazz:3*2")`} punchcard />
|
||||
|
||||
## Recap
|
||||
|
||||
Now we've learned the basics of the so called Mini-Notation, the rhythm language of Tidal.
|
||||
This is what we've leared so far:
|
||||
|
||||
| Concept | Syntax | Example |
|
||||
| ----------------- | -------- | -------------------------------------------------------------------------------- |
|
||||
| Sequence | space | <MiniRepl hideHeader client:visible tune={`sound("bd bd sd hh")`} /> |
|
||||
| Sample Number | :x | <MiniRepl hideHeader client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
|
||||
| Rests | ~ | <MiniRepl hideHeader client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
|
||||
| Sub-Sequences | \[\] | <MiniRepl hideHeader client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
|
||||
| Sub-Sub-Sequences | \[\[\]\] | <MiniRepl hideHeader client:visible tune={`sound("bd [metal [jazz sd]]")`} /> |
|
||||
| Speed up | \* | <MiniRepl hideHeader client:visible tune={`sound("bd sd*2 cp*3")`} /> |
|
||||
| Parallel | , | <MiniRepl hideHeader client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
|
||||
|
||||
The Mini-Notation is usually used inside some function. These are the functions we've seen so far:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | ----------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| sound | plays the sound of the given name | <MiniRepl hideHeader client:visible tune={`sound("bd sd")`} /> |
|
||||
| bank | selects the sound bank | <MiniRepl hideHeader client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
|
||||
| cpm | sets the tempo in cycles per minute | <MiniRepl hideHeader client:visible tune={`sound("bd sd").cpm(90)`} /> |
|
||||
| n | select sample number | <MiniRepl hideHeader client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
|
||||
|
||||
## Examples
|
||||
|
||||
**Basic rock beat**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd sd, hh*4").bank("RolandTR505").cpm(100/2)`} punchcard />
|
||||
|
||||
**Classic house**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd*2, ~ cp, [~ hh]*2").bank("RolandTR909")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Notice that the two patterns are extremely similar.
|
||||
Certain drum patterns are reused across genres.
|
||||
|
||||
</Box>
|
||||
|
||||
We Will Rock you
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("bd*2 cp").bank("RolandTR707").cpm(81/2)`} punchcard />
|
||||
|
||||
**Yellow Magic Orchestra - Firecracker**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound("bd sd, ~ ~ ~ hh ~ hh ~ ~, ~ perc ~ perc:1*2")
|
||||
.bank("RolandCompurhythm1000")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Imitation of a 16 step sequencer**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
[~ ~ oh ~ ] [~ ~ ~ ~ ] [~ ~ ~ ~ ] [~ ~ ~ ~ ],
|
||||
[hh hh ~ ~ ] [hh ~ hh ~ ] [hh ~ hh ~ ] [hh ~ hh ~ ],
|
||||
[~ ~ ~ ~ ] [cp ~ ~ ~ ] [~ ~ ~ ~ ] [cp ~ ~ ~ ],
|
||||
[bd ~ ~ ~ ] [~ ~ ~ bd] [~ ~ bd ~ ] [~ ~ ~ bd]
|
||||
\`).cpm(90/4)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Another one**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
[~ ~ ~ ~ ] [~ ~ ~ ~ ] [~ ~ ~ ~ ] [~ ~ oh:1 ~ ],
|
||||
[hh hh hh hh] [hh hh hh hh] [hh hh hh hh] [hh hh ~ ~ ],
|
||||
[~ ~ ~ ~ ] [cp ~ ~ ~ ] [~ ~ ~ ~ ] [~ cp ~ ~ ],
|
||||
[bd bd ~ ~ ] [~ ~ bd ~ ] [bd bd ~ bd ] [~ ~ ~ ~ ]
|
||||
\`).bank("RolandTR808").cpm(88/4)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Not your average drums**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`s(\`jazz*2,
|
||||
insect [crow metal] ~ ~,
|
||||
~ space:4 ~ space:1,
|
||||
~ wind\`)
|
||||
.cpm(100/2)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
Now that we know the basics of how to make beats, let's look at how we can play [notes](/workshop/first-notes)
|
||||
70
website/src/pages/workshop/getting-started.mdx
Normal file
70
website/src/pages/workshop/getting-started.mdx
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
title: Getting Started
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
|
||||
# Welcome
|
||||
|
||||
<img src="/icons/strudel_icon.png" className="w-32 animate-pulse md:float-right ml-8" />
|
||||
|
||||
Welcome to the Strudel documentation pages!
|
||||
You've come to the right place if you want to learn how to make music with code.
|
||||
|
||||
## What is Strudel?
|
||||
|
||||
With Strudel, you can expressively write dynamic music pieces.<br/>
|
||||
It is an official port of the [Tidal Cycles](https://tidalcycles.org/) pattern language to JavaScript.<br/>
|
||||
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.<br/>
|
||||
The best place to actually make music with Strudel is the [Strudel REPL](https://strudel.tidalcycles.org/)
|
||||
|
||||
<div className="clear-both" />
|
||||
|
||||
## What can you do with Strudel?
|
||||
|
||||
- live code music: make music with code in real time
|
||||
- algorithmic composition: compose music using tidal's unique approach to pattern manipulation
|
||||
- teaching: focussing on a low barrier of entry, Strudel is a good fit for teaching music and code at the same time.
|
||||
- integrate into your existing music setup: either via MIDI or OSC, you can use Strudel as a really flexible sequencer
|
||||
|
||||
## Example
|
||||
|
||||
Here is an example of how strudel can sound:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`stack(
|
||||
// drums
|
||||
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8")
|
||||
.speed(perlin.range(.8,.9)), // random sample speed variation
|
||||
// bassline
|
||||
"<a1 b1\*2 a1(3,8) e2>"
|
||||
.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
|
||||
.note() // wrap in "note"
|
||||
.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
|
||||
// chords
|
||||
"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand')
|
||||
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
|
||||
.add(perlin.range(0,.5)) // random pitch variation
|
||||
.note() // wrap in "note"
|
||||
.s('sawtooth') // waveform
|
||||
.gain(.16) // turn down
|
||||
.cutoff(500) // fixed cutoff
|
||||
.attack(1) // slowly fade in
|
||||
)
|
||||
.slow(3/2)`}
|
||||
/>
|
||||
|
||||
To hear more, go to the [Strudel REPL](https://strudel.tidalcycles.org/) and press shuffle to hear a random example pattern.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The best way to start learning Strudel is the workshop.
|
||||
If you're ready to dive in, let's start with your [first sounds](/workshop/first-sounds)
|
||||
3
website/src/pages/workshop/index.astro
Normal file
3
website/src/pages/workshop/index.astro
Normal file
@ -0,0 +1,3 @@
|
||||
<script is:inline>
|
||||
window.location.pathname = `/workshop/intro`;
|
||||
</script>
|
||||
181
website/src/pages/workshop/pattern-effects.mdx
Normal file
181
website/src/pages/workshop/pattern-effects.mdx
Normal file
@ -0,0 +1,181 @@
|
||||
---
|
||||
title: Pattern Effects
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
# Pattern Effects
|
||||
|
||||
Up until now, most of the functions we've seen are what other music programs are typically capable of: sequencing sounds, playing notes, controlling effects.
|
||||
|
||||
In this chapter, we are going to look at functions that are more unique to tidal.
|
||||
|
||||
**reverse patterns with rev**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 3] 2").sound("jazz").rev()`} />
|
||||
|
||||
**play pattern left and modify it right with jux**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`n("0 1 [4 3] 2").sound("jazz").jux(rev)`} />
|
||||
|
||||
This is the same as:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(0),
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(1).rev()
|
||||
)`}
|
||||
/>
|
||||
|
||||
Let's visualize what happens here:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(0).color("cyan"),
|
||||
n("0 1 [4 3] 2").sound("jazz").pan(1).color("magenta").rev()
|
||||
)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try commenting out one of the two by adding `//` before a line
|
||||
|
||||
</Box>
|
||||
|
||||
**multiple tempos**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`note("c2, eb3 g3 [bb3 c4]").sound("piano").slow("1,2,3")`} />
|
||||
|
||||
This is like doing
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1).color('cyan'),
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(2).color('magenta'),
|
||||
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(3).color('yellow')
|
||||
)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
Try commenting out one or more by adding `//` before a line
|
||||
|
||||
</Box>
|
||||
|
||||
**add**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c2 [eb3,g3]".add("<0 <1 -1>>"))
|
||||
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
If you add a number to a note, the note will be treated as if it was a number
|
||||
|
||||
</Box>
|
||||
|
||||
We can add as often as we like:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`note("c2 [eb3,g3]".add("<0 <1 -1>>").add("0,7"))
|
||||
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**add with scale**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<0 [2 4] <3 5> [~ <4 1>]>*2".add("<0 [0,2,4]>/4"))
|
||||
.scale("C5:minor").release(.5)
|
||||
.sound("gm_xylophone").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**time to stack**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`stack(
|
||||
n("<0 [2 4] <3 5> [~ <4 1>]>*2".add("<0 [0,2,4]>/4"))
|
||||
.scale("C5:minor")
|
||||
.sound("gm_xylophone")
|
||||
.room(.4).delay(.125),
|
||||
note("c2 [eb3,g3]".add("<0 <1 -1>>"))
|
||||
.adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass")
|
||||
.room(.5),
|
||||
n("0 1 [2 3] 2").sound("jazz").jux(rev).slow(2)
|
||||
)`}
|
||||
/>
|
||||
|
||||
**ply**
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh, bd rim").bank("RolandTR707").ply(2)`} punchcard />
|
||||
|
||||
this is like writing:
|
||||
|
||||
<MiniRepl hideHeader client:visible tune={`sound("hh*2, bd*2 rim*2").bank("RolandTR707")`} punchcard />
|
||||
|
||||
<Box>
|
||||
|
||||
Try patterning the `ply` function, for example using `"<1 2 1 3>"`
|
||||
|
||||
</Box>
|
||||
|
||||
**off**
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`n("<0 [4 <3 2>] <2 3> [~ 1]>"
|
||||
.off(1/8, x=>x.add(4))
|
||||
//.off(1/4, x=>x.add(7))
|
||||
).scale("<C5:minor Db5:mixolydian>/4")
|
||||
.s("triangle").room(.5).ds(".1:0").delay(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
In the notation `x=>x.`, the `x` is the shifted pattern, which where modifying.
|
||||
|
||||
</Box>
|
||||
|
||||
off is also useful for sounds:
|
||||
|
||||
<MiniRepl
|
||||
hideHeader
|
||||
client:visible
|
||||
tune={`s("bd sd,[~ hh]*2").bank("CasioRZ1")
|
||||
.off(1/8, x=>x.speed(1.5).gain(.25))`}
|
||||
/>
|
||||
|
||||
| name | description | example |
|
||||
| ---- | ------------------------------ | ---------------------------------------------------------------------------------------------- |
|
||||
| rev | reverse | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | split left/right, modify right | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | add numbers / notes | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | speed up each event n times | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | copy, shift time & modify | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
68
website/src/pages/workshop/recap.mdx
Normal file
68
website/src/pages/workshop/recap.mdx
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
title: Recap
|
||||
layout: ../../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
|
||||
# Workshop Recap
|
||||
|
||||
This page is just a listing of all functions covered in the workshop!
|
||||
|
||||
## Mini Notation
|
||||
|
||||
| Concept | Syntax | Example |
|
||||
| ----------------- | -------- | -------------------------------------------------------------------------------- |
|
||||
| Sequence | space | <MiniRepl hideHeader client:visible tune={`sound("bd bd sn hh")`} /> |
|
||||
| Sample Number | :x | <MiniRepl hideHeader client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
|
||||
| Rests | ~ | <MiniRepl hideHeader client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
|
||||
| Sub-Sequences | \[\] | <MiniRepl hideHeader client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
|
||||
| Sub-Sub-Sequences | \[\[\]\] | <MiniRepl hideHeader client:visible tune={`sound("bd [metal [jazz sn]]")`} /> |
|
||||
| Speed up | \* | <MiniRepl hideHeader client:visible tune={`sound("bd sn*2 cp*3")`} /> |
|
||||
| Parallel | , | <MiniRepl hideHeader client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
|
||||
| Slow down | \/ | <MiniRepl hideHeader client:visible tune={`note("[c a f e]/2")`} /> |
|
||||
| Alternate | \<\> | <MiniRepl hideHeader client:visible tune={`note("c <e g>")`} /> |
|
||||
| Elongate | @ | <MiniRepl hideHeader client:visible tune={`note("c@3 e")`} /> |
|
||||
| Replicate | ! | <MiniRepl hideHeader client:visible tune={`note("c!3 e")`} /> |
|
||||
|
||||
## Sounds
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | --------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| sound | plays the sound of the given name | <MiniRepl hideHeader client:visible tune={`sound("bd sd")`} /> |
|
||||
| bank | selects the sound bank | <MiniRepl hideHeader client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
|
||||
| n | select sample number | <MiniRepl hideHeader client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
|
||||
|
||||
## Notes
|
||||
|
||||
| Name | Description | Example |
|
||||
| --------- | ----------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| note | set pitch as number or letter | <MiniRepl hideHeader client:visible tune={`note("b g e c").sound("piano")`} /> |
|
||||
| n + scale | set note in scale | <MiniRepl hideHeader client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
|
||||
| stack | play patterns in parallel | <MiniRepl hideHeader client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
|
||||
|
||||
## Audio Effects
|
||||
|
||||
| name | example |
|
||||
| ----- | -------------------------------------------------------------------------------------------------- |
|
||||
| lpf | <MiniRepl hideHeader client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
|
||||
| vowel | <MiniRepl hideHeader client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
|
||||
| gain | <MiniRepl hideHeader client:visible tune={`s("hh*8").gain("[.25 1]*2")`} /> |
|
||||
| delay | <MiniRepl hideHeader client:visible tune={`s("bd rim").delay(.5)`} /> |
|
||||
| room | <MiniRepl hideHeader client:visible tune={`s("bd rim").room(.5)`} /> |
|
||||
| pan | <MiniRepl hideHeader client:visible tune={`s("bd rim").pan("0 1")`} /> |
|
||||
| speed | <MiniRepl hideHeader client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
|
||||
| range | <MiniRepl hideHeader client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
|
||||
|
||||
## Pattern Effects
|
||||
|
||||
| name | description | example |
|
||||
| ---- | ----------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| cpm | sets the tempo in cycles per minute | <MiniRepl hideHeader client:visible tune={`sound("bd sd").cpm(90)`} /> |
|
||||
| fast | speed up | <MiniRepl hideHeader client:visible tune={`sound("bd sd").fast(2)`} /> |
|
||||
| slow | slow down | <MiniRepl hideHeader client:visible tune={`sound("bd sd").slow(2)`} /> |
|
||||
| rev | reverse | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | split left/right, modify right | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | add numbers / notes | <MiniRepl hideHeader client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | speed up each event n times | <MiniRepl hideHeader client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | copy, shift time & modify | <MiniRepl hideHeader client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
@ -122,7 +122,7 @@ export function Header({ context }) {
|
||||
{!isEmbedded && (
|
||||
<a
|
||||
title="learn"
|
||||
href="./learn/getting-started"
|
||||
href="./workshop/getting-started"
|
||||
className={cx('hover:opacity-50 flex items-center space-x-1', !isEmbedded ? 'p-2' : 'px-2')}
|
||||
>
|
||||
<AcademicCapIcon className="w-6 h-6" />
|
||||
|
||||
@ -22,12 +22,8 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
#code .cm-content {
|
||||
padding-top: 12px !important;
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
|
||||
#code .cm-scroller {
|
||||
padding-top: 10px !important;
|
||||
padding-bottom: 50vh;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ export function Repl({ embedded = false }) {
|
||||
e.preventDefault();
|
||||
flash(view);
|
||||
await activateCode();
|
||||
} else if (e.key === '.') {
|
||||
} else if (e.key === '.' || e.keyCode === 'Period') {
|
||||
stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
@ -22,8 +22,96 @@ export async function prebake() {
|
||||
tag: 'drum-machines',
|
||||
}),
|
||||
samples(`./EmuSP12.json`, `./EmuSP12/`, { prebake: true, tag: 'drum-machines' }),
|
||||
// samples('github:tidalcycles/Dirt-Samples/master'),
|
||||
samples(
|
||||
{
|
||||
casio: ['casio/high.wav', 'casio/low.wav', 'casio/noise.wav'],
|
||||
crow: ['crow/000_crow.wav', 'crow/001_crow2.wav', 'crow/002_crow3.wav', 'crow/003_crow4.wav'],
|
||||
insect: [
|
||||
'insect/000_everglades_conehead.wav',
|
||||
'insect/001_robust_shieldback.wav',
|
||||
'insect/002_seashore_meadow_katydid.wav',
|
||||
],
|
||||
wind: [
|
||||
'wind/000_wind1.wav',
|
||||
'wind/001_wind10.wav',
|
||||
'wind/002_wind2.wav',
|
||||
'wind/003_wind3.wav',
|
||||
'wind/004_wind4.wav',
|
||||
'wind/005_wind5.wav',
|
||||
'wind/006_wind6.wav',
|
||||
'wind/007_wind7.wav',
|
||||
'wind/008_wind8.wav',
|
||||
'wind/009_wind9.wav',
|
||||
],
|
||||
jazz: [
|
||||
'jazz/000_BD.wav',
|
||||
'jazz/001_CB.wav',
|
||||
'jazz/002_FX.wav',
|
||||
'jazz/003_HH.wav',
|
||||
'jazz/004_OH.wav',
|
||||
'jazz/005_P1.wav',
|
||||
'jazz/006_P2.wav',
|
||||
'jazz/007_SN.wav',
|
||||
],
|
||||
metal: [
|
||||
'metal/000_0.wav',
|
||||
'metal/001_1.wav',
|
||||
'metal/002_2.wav',
|
||||
'metal/003_3.wav',
|
||||
'metal/004_4.wav',
|
||||
'metal/005_5.wav',
|
||||
'metal/006_6.wav',
|
||||
'metal/007_7.wav',
|
||||
'metal/008_8.wav',
|
||||
'metal/009_9.wav',
|
||||
],
|
||||
east: [
|
||||
'east/000_nipon_wood_block.wav',
|
||||
'east/001_ohkawa_mute.wav',
|
||||
'east/002_ohkawa_open.wav',
|
||||
'east/003_shime_hi.wav',
|
||||
'east/004_shime_hi_2.wav',
|
||||
'east/005_shime_mute.wav',
|
||||
'east/006_taiko_1.wav',
|
||||
'east/007_taiko_2.wav',
|
||||
'east/008_taiko_3.wav',
|
||||
],
|
||||
space: [
|
||||
'space/000_0.wav',
|
||||
'space/001_1.wav',
|
||||
'space/002_11.wav',
|
||||
'space/003_12.wav',
|
||||
'space/004_13.wav',
|
||||
'space/005_14.wav',
|
||||
'space/006_15.wav',
|
||||
'space/007_16.wav',
|
||||
'space/008_17.wav',
|
||||
'space/009_18.wav',
|
||||
'space/010_2.wav',
|
||||
'space/011_3.wav',
|
||||
'space/012_4.wav',
|
||||
'space/013_5.wav',
|
||||
'space/014_6.wav',
|
||||
'space/015_7.wav',
|
||||
'space/016_8.wav',
|
||||
'space/017_9.wav',
|
||||
],
|
||||
numbers: [
|
||||
'numbers/0.wav',
|
||||
'numbers/1.wav',
|
||||
'numbers/2.wav',
|
||||
'numbers/3.wav',
|
||||
'numbers/4.wav',
|
||||
'numbers/5.wav',
|
||||
'numbers/6.wav',
|
||||
'numbers/7.wav',
|
||||
'numbers/8.wav',
|
||||
],
|
||||
},
|
||||
'github:tidalcycles/Dirt-Samples/master/',
|
||||
),
|
||||
]);
|
||||
// await samples('github:tidalcycles/Dirt-Samples/master');
|
||||
}
|
||||
|
||||
const maxPan = noteToMidi('C8');
|
||||
|
||||
@ -7,6 +7,11 @@
|
||||
"noImplicitAny": false,
|
||||
"types": [
|
||||
"vite-plugin-pwa/client"
|
||||
]
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@components/*": ["src/components/*"],
|
||||
"@src/*": ["src/*"],
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user