Merge branch 'main' into pattern_selection
This commit is contained in:
commit
500ddf3946
111 changed files with 1165 additions and 538 deletions
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
|
||||
type Props = CollectionEntry<'blog'>['data'];
|
||||
type Props = { post: CollectionEntry<'blog'> };
|
||||
|
||||
const { post } = Astro.props;
|
||||
const { Content } = await post.render();
|
||||
|
|
@ -9,7 +9,7 @@ import { format } from 'date-fns';
|
|||
---
|
||||
|
||||
<article
|
||||
class="prose max-w-none prose-headings:font-sans prose-headings:font-black prose-headings:text-slate-900 dark:prose-headings:text-gray-200 dark:text-gray-400 dark:prose-strong:text-gray-400 dark:prose-code:text-slate-400 dark:prose-a:text-gray-300 prose-a:text-slate-900 prose-blockquote:text-slate-800 dark:prose-blockquote:text-slate-400"
|
||||
class="prose max-w-none prose-headings:font-sans prose-headings:font-black prose-headings:text-slate-900 dark:prose-headings:text-gray-200 dark:text-gray-400 dark:prose-strong:text-gray-400 dark:prose-code:text-slate-400 dark:prose-a:text-gray-300 prose-a:text-slate-900 prose-blockquote:text-slate-800 dark:prose-blockquote:text-slate-400 border-b-4 border-lineHighlight pt-4"
|
||||
>
|
||||
<div class="pb-2">
|
||||
<div class="md:flex justify-between">
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
|
|||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<link rel="alternate" type="application/rss+xml" title={`RSS Feed for strudel.cc`} href="/rss.xml" />
|
||||
<link rel="icon" type="image/svg+xml" href={`${baseNoTrailing}/favicon.ico`} />
|
||||
|
||||
<meta
|
||||
|
|
@ -24,7 +24,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL
|
|||
<base href={BASE_URL} />
|
||||
|
||||
<!-- Scrollable a11y code helper -->
|
||||
<script src{`${baseNoTrailing}/make-scrollable-code-focusable.js`} is:inline></script>
|
||||
<script {`${baseNoTrailing}/make-scrollable-code-focusable.js`} is:inline></script>
|
||||
|
||||
<script src="/src/pwa.ts"></script>
|
||||
<!-- this does not work for some reason: -->
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
import { SITE, OPEN_GRAPH, Frontmatter } from '../config';
|
||||
import { SITE, OPEN_GRAPH, type Frontmatter } from '../config';
|
||||
|
||||
export interface Props {
|
||||
frontmatter: Frontmatter;
|
||||
|
|
@ -25,11 +25,3 @@ const imageAlt = frontmatter.image?.alt ?? OPEN_GRAPH.image.alt;
|
|||
<meta property="og:image:alt" content={imageAlt} />
|
||||
<meta name="description" property="og:description" content={frontmatter.description ?? SITE.description} />
|
||||
<meta property="og:site_name" content={SITE.title} />
|
||||
|
||||
<!-- Twitter Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:site" content={OPEN_GRAPH.twitter} />
|
||||
<meta name="twitter:title" content={formattedContentTitle} />
|
||||
<meta name="twitter:description" content={frontmatter.description ?? SITE.description} />
|
||||
<meta name="twitter:image" content={canonicalImageSrc} />
|
||||
<meta name="twitter:image:alt" content={imageAlt} />
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import useEvent from '@src/useEvent.mjs';
|
||||
import useFrame from '@src/useFrame.mjs';
|
||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { midi2note } from '@strudel.cycles/core';
|
||||
import { getAudioContext } from '@strudel/webaudio';
|
||||
import { midi2note } from '@strudel/core';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import Claviature from '@components/Claviature';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { colorMap } from '@strudel.cycles/core/color.mjs';
|
||||
import { colorMap } from '@strudel/core/color.mjs';
|
||||
import React from 'react';
|
||||
|
||||
const Colors = () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,19 @@
|
|||
export function Icon({ type }) {
|
||||
if (type === 'skip') {
|
||||
// !Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.
|
||||
return (
|
||||
<svg
|
||||
fillRule="evenodd"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="16"
|
||||
width="10"
|
||||
viewBox="0 0 320 512"
|
||||
>
|
||||
<path d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416V96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4l192 160L256 241V96c0-17.7 14.3-32 32-32s32 14.3 32 32V416c0 17.7-14.3 32-32 32s-32-14.3-32-32V271l-11.5 9.6-192 160z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||
{
|
||||
|
|
@ -31,6 +46,13 @@ export function Icon({ type }) {
|
|||
clipRule="evenodd"
|
||||
/>
|
||||
),
|
||||
skip: (
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M52.5 440.6c-9.5 7.9-22.8 9.7-34.1 4.4S0 428.4 0 416V96C0 83.6 7.2 72.3 18.4 67s24.5-3.6 34.1 4.4l192 160L256 241V96c0-17.7 14.3-32 32-32s32 14.3 32 32V416c0 17.7-14.3 32-32 32s-32-14.3-32-32V271l-11.5 9.6-192 160z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
),
|
||||
}[type]
|
||||
}
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useState, useRef, useCallback, useMemo, useEffect } from 'react';
|
||||
import { Icon } from './Icon';
|
||||
import { silence, getPunchcardPainter, noteToMidi } from '@strudel.cycles/core';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import { silence, getPunchcardPainter, noteToMidi, _mod } from '@strudel/core';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel/webaudio';
|
||||
import { StrudelMirror } from '@strudel/codemirror';
|
||||
// import { prebake } from '@strudel/repl';
|
||||
import { prebake } from '../repl/prebake.mjs';
|
||||
|
|
@ -10,14 +10,16 @@ import { loadModules } from '../repl/util.mjs';
|
|||
import Claviature from '@components/Claviature';
|
||||
import useClient from '@src/useClient.mjs';
|
||||
|
||||
let prebaked, modulesLoading;
|
||||
let prebaked, modulesLoading, audioLoading;
|
||||
if (typeof window !== 'undefined') {
|
||||
prebaked = prebake();
|
||||
modulesLoading = loadModules();
|
||||
audioLoading = initAudioOnFirstClick();
|
||||
}
|
||||
|
||||
export function MiniRepl({
|
||||
tune: code,
|
||||
tune,
|
||||
tunes,
|
||||
hideHeader = false,
|
||||
canvasHeight = 100,
|
||||
onTrigger,
|
||||
|
|
@ -26,6 +28,7 @@ export function MiniRepl({
|
|||
claviature,
|
||||
claviatureLabels,
|
||||
}) {
|
||||
const code = tunes ? tunes[0] : tune;
|
||||
const id = useMemo(() => s4(), []);
|
||||
const canvasId = useMemo(() => `canvas-${id}`, [id]);
|
||||
const shouldDraw = !!punchcard || !!claviature;
|
||||
|
|
@ -75,7 +78,7 @@ export function MiniRepl({
|
|||
}
|
||||
return pat;
|
||||
},
|
||||
prebake: async () => Promise.all([modulesLoading, prebaked]),
|
||||
prebake: async () => Promise.all([modulesLoading, prebaked, audioLoading]),
|
||||
onUpdateState: (state) => {
|
||||
setReplState({ ...state });
|
||||
},
|
||||
|
|
@ -91,6 +94,14 @@ export function MiniRepl({
|
|||
const containerRef = useRef();
|
||||
const client = useClient();
|
||||
|
||||
const [tuneIndex, setTuneIndex] = useState(0);
|
||||
const changeTune = (index) => {
|
||||
index = _mod(index, tunes.length);
|
||||
setTuneIndex(index);
|
||||
editorRef.current?.setCode(tunes[index]);
|
||||
editorRef.current?.evaluate();
|
||||
};
|
||||
|
||||
if (!client) {
|
||||
return <pre>{code}</pre>;
|
||||
}
|
||||
|
|
@ -119,6 +130,28 @@ export function MiniRepl({
|
|||
<Icon type="refresh" />
|
||||
</button>
|
||||
</div>
|
||||
{tunes && (
|
||||
<div className="flex">
|
||||
<button
|
||||
className={
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background'
|
||||
}
|
||||
onClick={() => changeTune(tuneIndex - 1)}
|
||||
>
|
||||
<div className="rotate-180">
|
||||
<Icon type="skip" />
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
className={
|
||||
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background'
|
||||
}
|
||||
onClick={() => changeTune(tuneIndex + 1)}
|
||||
>
|
||||
<Icon type="skip" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="overflow-auto relative p-1">
|
||||
|
|
|
|||
81
website/src/examples.mjs
Normal file
81
website/src/examples.mjs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
export const examples = [
|
||||
`// "coastline" @by eddyflux
|
||||
await samples('github:eddyflux/crate')
|
||||
setcps(.75)
|
||||
let chords = chord("<Bbm9 Fm9>/4").dict('ireal')
|
||||
stack(
|
||||
stack( // DRUMS
|
||||
s("bd").struct("<[x*<1 2> [~@3 x]] x>"),
|
||||
s("~ [rim, sd:<2 3>]").room("<0 .2>"),
|
||||
n("[0 <1 3>]*<2!3 4>").s("hh"),
|
||||
s("rd:<1!3 2>*2").mask("<0 0 1 1>/16").gain(.5)
|
||||
).bank('crate')
|
||||
.mask("<[0 1] 1 1 1>/16".early(.5))
|
||||
, // CHORDS
|
||||
chords.offset(-1).voicing().s("gm_epiano1:1")
|
||||
.phaser(4).room(.5)
|
||||
, // MELODY
|
||||
n("<0!3 1*2>").set(chords).mode("root:g2")
|
||||
.voicing().s("gm_acoustic_bass"),
|
||||
chords.n("[0 <4 3 <2 5>>*2](<3 5>,8)")
|
||||
.set(x).anchor("D5").voicing()
|
||||
.segment(4).clip(rand.range(.4,.8))
|
||||
.room(.75).shape(.3).delay(.25)
|
||||
.fm(sine.range(3,8).slow(8))
|
||||
.lpf(sine.range(500,1000).slow(8)).lpq(5)
|
||||
.rarely(ply("2")).chunk(4, fast(2))
|
||||
.gain(perlin.range(.6, .9))
|
||||
.mask("<0 1 1 0>/16")
|
||||
)
|
||||
.late("[0 .01]*4").late("[0 .01]*2").size(4)`,
|
||||
`// "broken cut 1" @by froos
|
||||
|
||||
await samples('github:tidalcycles/Dirt-Samples/master')
|
||||
samples({
|
||||
'slap': 'https://cdn.freesound.org/previews/495/495416_10350281-lq.mp3',
|
||||
'whirl': 'https://cdn.freesound.org/previews/495/495313_10350281-lq.mp3',
|
||||
'attack': 'https://cdn.freesound.org/previews/494/494947_10350281-lq.mp3'
|
||||
})
|
||||
|
||||
setcps(1.25)
|
||||
|
||||
note("[c2 ~](3,8)*2,eb,g,bb,d").s("sawtooth")
|
||||
.noise(0.3)
|
||||
.lpf(perlin.range(800,2000).mul(0.6))
|
||||
.lpenv(perlin.range(1,5)).lpa(.25).lpd(.1).lps(0)
|
||||
.add.mix(note("<0!3 [1 <4!3 12>]>")).late(.5)
|
||||
.vib("4:.2")
|
||||
.room(1).roomsize(4).slow(4)
|
||||
.stack(
|
||||
s("bd").late("<0.01 .251>"),
|
||||
s("breaks165:1/2").fit()
|
||||
.chop(4).sometimesBy(.4, ply("2"))
|
||||
.sometimesBy(.1, ply("4")).release(.01)
|
||||
.gain(1.5).sometimes(mul(speed("1.05"))).cut(1)
|
||||
,
|
||||
s("<whirl attack>?").delay(".8:.1:.8").room(2).slow(8).cut(2),
|
||||
).reset("<x@30 [x*[8 [8 [16 32]]]]@2>".late(2))`,
|
||||
`// "acidic tooth" @by eddyflux
|
||||
setcps(1)
|
||||
stack(
|
||||
note("[<g1 f1>/8](<3 5>,8)")
|
||||
.clip(perlin.range(.15,1.5))
|
||||
.release(.1)
|
||||
.s("sawtooth")
|
||||
.lpf(sine.range(400,800).slow(16))
|
||||
.lpq(cosine.range(6,14).slow(3))
|
||||
.lpenv(sine.mul(4).slow(4))
|
||||
.lpd(.2).lpa(.02)
|
||||
.ftype('24db')
|
||||
.rarely(add(note(12)))
|
||||
.room(.2).shape(.3).postgain(.5)
|
||||
.superimpose(x=>x.add(note(12)).delay(.5).bpf(1000))
|
||||
.gain("[.2 1@3]*2") // fake sidechain
|
||||
,
|
||||
stack(
|
||||
s("bd*2").mask("<0@4 1@16>"),
|
||||
s("hh*8").gain(saw.mul(saw.fast(2))).clip(sine)
|
||||
.mask("<0@8 1@16>")
|
||||
).bank('RolandTR909')
|
||||
)`,
|
||||
];
|
||||
|
|
@ -32,7 +32,18 @@ const posts = (await getCollection('blog')).sort((a, b) => compareDesc(a.data.da
|
|||
<LeftSidebar currentPage={currentPage} />
|
||||
</aside>
|
||||
<PageContent>
|
||||
{posts.map((post) => <BlogPost post={post} />)}
|
||||
<div class="border-b-4 border-lineHighlight py-4">
|
||||
<h1>Strudel Blog</h1>
|
||||
<p>
|
||||
Welcome to the Strudel Blog, where we will keep you updated with the latest changes and things happening
|
||||
in the strudelsphere. You can subscribe to this blog using <a target="_blank" href="/rss.xml"
|
||||
>this rss link</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-8">
|
||||
{posts.map((post) => <BlogPost post={post} />)}
|
||||
</div>
|
||||
</PageContent>
|
||||
<aside class="fixed right-0 h-full overflow-auto pr-4 pl-0 pb-16 hidden xl:block" title="Table of Contents">
|
||||
<RightSidebar
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ layout: ../../../layouts/MainLayout.astro
|
|||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import { midi2note } from '@strudel.cycles/core/';
|
||||
import { midi2note } from '@strudel/core/';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { createCanvas } from 'canvas';
|
||||
import { pianoroll } from '@strudel.cycles/core';
|
||||
import { evaluate } from '@strudel.cycles/transpiler';
|
||||
import { pianoroll } from '@strudel/core';
|
||||
import { evaluate } from '@strudel/transpiler';
|
||||
import '../../../../test/runtime.mjs';
|
||||
import * as tunes from '../../repl/tunes.mjs';
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,60 @@ There is one filter envelope for each filter type and thus one set of envelope f
|
|||
|
||||
<JsDoc client:idle name="lpenv" h={0} />
|
||||
|
||||
# Pitch Envelope
|
||||
|
||||
You can also control the pitch with envelopes!
|
||||
Pitch envelopes can breathe life into static sounds:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`n("<-4,0 5 2 1>*<2!3 4>")
|
||||
.scale("<C F>/8:pentatonic")
|
||||
.s("gm_electric_guitar_jazz")
|
||||
.penv("<.5 0 7 -2>*2").vib("4:.1")
|
||||
.phaser(2).delay(.25).room(.3)
|
||||
.size(4).fast(.75)`}
|
||||
/>
|
||||
|
||||
You also create some lovely chiptune-style sounds:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`n(run("<4 8>/16")).jux(rev)
|
||||
.chord("<C^7 <Db^7 Fm7>>")
|
||||
.dict('ireal')
|
||||
.voicing().add(note("<0 1>/8"))
|
||||
.dec(.1).room(.2)
|
||||
.segment("<4 [2 8]>")
|
||||
.penv("<0 <2 -2>>").patt(.02)`}
|
||||
/>
|
||||
|
||||
Let's break down all pitch envelope controls:
|
||||
|
||||
## pattack
|
||||
|
||||
<JsDoc client:idle name="pattack" h={0} />
|
||||
|
||||
## pdecay
|
||||
|
||||
<JsDoc client:idle name="pdecay" h={0} />
|
||||
|
||||
## prelease
|
||||
|
||||
<JsDoc client:idle name="prelease" h={0} />
|
||||
|
||||
## penv
|
||||
|
||||
<JsDoc client:idle name="penv" h={0} />
|
||||
|
||||
## pcurve
|
||||
|
||||
<JsDoc client:idle name="pcurve" h={0} />
|
||||
|
||||
## panchor
|
||||
|
||||
<JsDoc client:idle name="panchor" h={0} />
|
||||
|
||||
# Dynamics
|
||||
|
||||
## gain
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ layout: ../../layouts/MainLayout.astro
|
|||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { JsDoc } from '../../docs/JsDoc';
|
||||
import { samples } from '@strudel.cycles/webaudio';
|
||||
|
||||
see https://strudel.cc/?zMEo5kowGrFc
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ layout: ../../layouts/MainLayout.astro
|
|||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { JsDoc } from '../../docs/JsDoc';
|
||||
import { samples } from '@strudel.cycles/webaudio';
|
||||
|
||||
Note:
|
||||
|
||||
|
|
|
|||
19
website/src/pages/rss.xml.js
Normal file
19
website/src/pages/rss.xml.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import rss from '@astrojs/rss';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
export async function GET(context) {
|
||||
const posts = (await getCollection('blog')).filter((p) => !p.data.draft);
|
||||
const options = {
|
||||
title: 'Strudel Blog',
|
||||
description:
|
||||
'The Strudel Blog will keep you updated with the latest changes and things happening in the strudelsphere.',
|
||||
site: context.site,
|
||||
items: posts.map((post) => ({
|
||||
link: `/blog/#${post.slug}`,
|
||||
title: post.data.title,
|
||||
pubDate: post.data.date,
|
||||
description: post.data.description,
|
||||
})),
|
||||
};
|
||||
return rss(options);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { createCanvas } from 'canvas';
|
||||
import { pianoroll } from '@strudel.cycles/core';
|
||||
import { evaluate } from '@strudel.cycles/transpiler';
|
||||
import { pianoroll } from '@strudel/core';
|
||||
import { evaluate } from '@strudel/transpiler';
|
||||
import '../../../../test/runtime.mjs';
|
||||
import { getMyPatterns } from '../../my_patterns';
|
||||
|
||||
|
|
@ -14,10 +14,7 @@ export async function GET({ params, request }) {
|
|||
const ctx = canvas.getContext('2d');
|
||||
pianoroll({ time: 4, haps, ctx, playhead: 1, fold: 1, background: 'transparent', playheadColor: 'transparent' });
|
||||
const buffer = canvas.toBuffer('image/png');
|
||||
return {
|
||||
body: buffer,
|
||||
encoding: 'binary',
|
||||
};
|
||||
return new Response(buffer);
|
||||
}
|
||||
export async function getStaticPaths() {
|
||||
const patterns = await getMyPatterns();
|
||||
|
|
|
|||
|
|
@ -16,16 +16,16 @@ The purpose of the multiple packages is to
|
|||
|
||||
## Overview
|
||||
|
||||
[See the latest published packages on npm](https://www.npmjs.com/search?q=%40strudel.cycles).
|
||||
[See the latest published packages on npm](https://www.npmjs.com/search?q=%40strudel).
|
||||
Here is an overview of all the packages:
|
||||
|
||||
### Essential Packages
|
||||
|
||||
These package are the most essential. You might want to use all of those if you're using strudel in your project:
|
||||
|
||||
- [core](https://github.com/tidalcycles/strudel/tree/main/packages/core#strudelcyclescore): tidal pattern engine with core primitives
|
||||
- [mini](https://github.com/tidalcycles/strudel/tree/main/packages/mini#strudelcyclesmini): mini notation parser + core bindings
|
||||
- [transpiler](https://github.com/tidalcycles/strudel/tree/main/packages/transpiler#strudelcyclestranspiler): user code transpiler. syntax sugar + highlighting
|
||||
- [core](https://github.com/tidalcycles/strudel/tree/main/packages/core#strudelcore): tidal pattern engine with core primitives
|
||||
- [mini](https://github.com/tidalcycles/strudel/tree/main/packages/mini#strudelmini): mini notation parser + core bindings
|
||||
- [transpiler](https://github.com/tidalcycles/strudel/tree/main/packages/transpiler#strudeltranspiler): user code transpiler. syntax sugar + highlighting
|
||||
|
||||
### Language Extensions
|
||||
|
||||
|
|
@ -38,23 +38,24 @@ These packages extend the pattern language by specific functions
|
|||
|
||||
These packages provide bindings for different ways to output strudel patterns:
|
||||
|
||||
- [webaudio](https://github.com/tidalcycles/strudel/tree/main/packages/webaudio#strudelcycleswebaudio): the default webaudio output
|
||||
- [osc](https://github.com/tidalcycles/strudel/tree/main/packages/osc#strudelcyclesosc): bindings to communicate via OSC
|
||||
- [midi](https://github.com/tidalcycles/strudel/tree/main/packages/midi#strudelcyclesmidi): webmidi bindings
|
||||
- [csound](https://github.com/tidalcycles/strudel/tree/main/packages/csound#strudelcyclescsound): csound bindings
|
||||
- [soundfonts](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelcyclessoundfonts): Soundfont support
|
||||
- [serial](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelcyclesserial): webserial bindings
|
||||
- [webaudio](https://github.com/tidalcycles/strudel/tree/main/packages/webaudio#strudelwebaudio): the default webaudio output
|
||||
- [osc](https://github.com/tidalcycles/strudel/tree/main/packages/osc#strudelosc): bindings to communicate via OSC
|
||||
- [midi](https://github.com/tidalcycles/strudel/tree/main/packages/midi#strudelmidi): webmidi bindings
|
||||
- [csound](https://github.com/tidalcycles/strudel/tree/main/packages/csound#strudelcsound): csound bindings
|
||||
- [soundfonts](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelsoundfonts): Soundfont support
|
||||
- [serial](https://github.com/tidalcycles/strudel/tree/main/packages/serial#strudelserial): webserial bindings
|
||||
|
||||
### Others
|
||||
|
||||
- [embed](https://github.com/tidalcycles/strudel/tree/main/packages/embed#strudelcyclesembed): embeddable REPL web component
|
||||
- [react](https://github.com/tidalcycles/strudel/tree/main/packages/react#strudelcyclesreact): react hooks and components for strudel
|
||||
- [embed](https://github.com/tidalcycles/strudel/tree/main/packages/embed#strudelembed): embeddable REPL web component
|
||||
|
||||
### No Longer Maintained
|
||||
|
||||
- [react](https://www.npmjs.com/package/@strudel.cycles/react): react hooks and components for strudel
|
||||
- [eval](https://www.npmjs.com/package/@strudel.cycles/eval): old code transpiler
|
||||
- [tone](https://www.npmjs.com/package/@strudel.cycles/tone): bindings for Tone.js instruments and effects
|
||||
- [webdirt](https://www.npmjs.com/package/@strudel.cycles/webdirt): webdirt bindings, replaced by webaudio package
|
||||
- any `@strudel.cycles/*` packages have been renamed to `@strudel/*` since version 0.10.0.
|
||||
|
||||
## Tools
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ layout: ../../layouts/MainLayout.astro
|
|||
---
|
||||
|
||||
import { MiniRepl } from '@src/docs/MiniRepl';
|
||||
import { midi2note } from '@strudel.cycles/core/';
|
||||
import { midi2note } from '@strudel/core';
|
||||
import Box from '@components/Box.astro';
|
||||
import QA from '@components/QA';
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ layout: ../../layouts/MainLayout.astro
|
|||
---
|
||||
|
||||
import { MiniRepl } from '../../docs/MiniRepl';
|
||||
import { examples } from '../../examples.mjs';
|
||||
|
||||
# Welcome
|
||||
|
||||
|
|
@ -29,40 +30,13 @@ The best place to actually make music with Strudel is the [Strudel REPL](https:/
|
|||
- 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
|
||||
## Examples
|
||||
|
||||
Here is an example of how strudel can sound:
|
||||
Here are some examples 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)`}
|
||||
/>
|
||||
<MiniRepl client:idle tunes={examples} />
|
||||
|
||||
To hear more, go to the [Strudel REPL](https://strudel.cc/) and press shuffle to hear a random example pattern.
|
||||
These examples cannot fully encompass the variety of things you can do, so [check out the showcase](/intro/showcase/) for some videos of how people use Strudel.
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ 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 { code2hash, getDrawContext, logger, silence } from '@strudel.cycles/core';
|
||||
import { code2hash, getDrawContext, logger, silence } from '@strudel/core';
|
||||
import cx from '@src/cx.mjs';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { getAudioContext, initAudioOnFirstClick, webaudioOutput } from '@strudel.cycles/webaudio';
|
||||
import { transpiler } from '@strudel/transpiler';
|
||||
import { getAudioContext, initAudioOnFirstClick, webaudioOutput } from '@strudel/webaudio';
|
||||
import { defaultAudioDeviceName } from '../settings.mjs';
|
||||
import { getAudioDevices, setAudioDevice } from './util.mjs';
|
||||
import { StrudelMirror, defaultSettings } from '@strudel/codemirror';
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
onTriggerSample,
|
||||
getAudioContext,
|
||||
loadBuffer,
|
||||
} from '@strudel.cycles/webaudio';
|
||||
} from '@strudel/webaudio';
|
||||
|
||||
let TAURI;
|
||||
if (typeof window !== 'undefined') {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { registerSound, onTriggerSample } from '@strudel.cycles/webaudio';
|
||||
import { registerSound, onTriggerSample } from '@strudel/webaudio';
|
||||
import { isAudioFile } from './files.mjs';
|
||||
import { logger } from '@strudel.cycles/core';
|
||||
import { logger } from '@strudel/core';
|
||||
|
||||
//utilites for writing and reading to the indexdb
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import XMarkIcon from '@heroicons/react/20/solid/XMarkIcon';
|
||||
import { logger } from '@strudel.cycles/core';
|
||||
import { logger } from '@strudel/core';
|
||||
import useEvent from '@src/useEvent.mjs';
|
||||
import cx from '@src/cx.mjs';
|
||||
import { nanoid } from 'nanoid';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import useEvent from '@src/useEvent.mjs';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { getAudioContext, soundMap, connectToDestination } from '@strudel.cycles/webaudio';
|
||||
import { getAudioContext, soundMap, connectToDestination } from '@strudel/webaudio';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { settingsMap, useSettings } from '../../settings.mjs';
|
||||
import { ButtonGroup } from './Forms.jsx';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Pattern, noteToMidi, valueToMidi } from '@strudel.cycles/core';
|
||||
import { Pattern, noteToMidi, valueToMidi } from '@strudel/core';
|
||||
|
||||
const maxPan = noteToMidi('C8');
|
||||
const panwidth = (pan, width) => pan * width + (1 - width) / 2;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Pattern, noteToMidi, valueToMidi } from '@strudel.cycles/core';
|
||||
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio';
|
||||
import { Pattern, noteToMidi, valueToMidi } from '@strudel/core';
|
||||
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio';
|
||||
import { registerSamplesFromDB } from './idbutils.mjs';
|
||||
import './piano.mjs';
|
||||
import './files.mjs';
|
||||
|
|
@ -15,10 +15,10 @@ export async function prebake() {
|
|||
registerZZFXSounds(),
|
||||
registerSamplesFromDB(),
|
||||
//registerSoundfonts(),
|
||||
// need dynamic import here, because importing @strudel.cycles/soundfonts fails on server:
|
||||
// => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically
|
||||
// need dynamic import here, because importing @strudel/soundfonts fails on server:
|
||||
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
|
||||
// seems to be a problem with soundfont2
|
||||
import('@strudel.cycles/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
|
||||
import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
|
||||
samples(`${baseNoTrailing}/piano.json`, `${baseNoTrailing}/piano/`, { prebake: true }),
|
||||
// https://github.com/sgossner/VCSL/
|
||||
// https://api.github.com/repositories/126427031/contents/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { controls, evalScope, hash2code, logger } from '@strudel.cycles/core';
|
||||
import { controls, evalScope, hash2code, logger } from '@strudel/core';
|
||||
import { settingPatterns, defaultAudioDeviceName } from '../settings.mjs';
|
||||
import { getAudioContext, initializeAudioOutput, setDefaultAudioContext } from '@strudel.cycles/webaudio';
|
||||
import { getAudioContext, initializeAudioOutput, setDefaultAudioContext } from '@strudel/webaudio';
|
||||
|
||||
import { isTauri } from '../tauri.mjs';
|
||||
import './Repl.css';
|
||||
|
|
@ -61,16 +61,16 @@ export function getRandomTune() {
|
|||
|
||||
export function loadModules() {
|
||||
let modules = [
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/xen'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel/core'),
|
||||
import('@strudel/tonal'),
|
||||
import('@strudel/mini'),
|
||||
import('@strudel/xen'),
|
||||
import('@strudel/webaudio'),
|
||||
import('@strudel/codemirror'),
|
||||
import('@strudel/hydra'),
|
||||
import('@strudel.cycles/serial'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
import('@strudel.cycles/csound'),
|
||||
import('@strudel/serial'),
|
||||
import('@strudel/soundfonts'),
|
||||
import('@strudel/csound'),
|
||||
];
|
||||
if (isTauri()) {
|
||||
modules = modules.concat([
|
||||
|
|
@ -79,7 +79,7 @@ export function loadModules() {
|
|||
import('@strudel/desktopbridge/oscbridge.mjs'),
|
||||
]);
|
||||
} else {
|
||||
modules = modules.concat([import('@strudel.cycles/midi'), import('@strudel.cycles/osc')]);
|
||||
modules = modules.concat([import('@strudel/midi'), import('@strudel/osc')]);
|
||||
}
|
||||
|
||||
return evalScope(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { persistentMap } from '@nanostores/persistent';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { register } from '@strudel.cycles/core';
|
||||
import { register } from '@strudel/core';
|
||||
|
||||
|
||||
export const defaultAudioDeviceName = 'System Standard';
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue