diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed1d0d3d..61e3a8b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -490,6 +490,9 @@ importers: '@astrojs/react': specifier: ^3.0.9 version: 3.0.9(@types/react-dom@18.2.18)(@types/react@18.2.46)(react-dom@18.2.0)(react@18.2.0)(vite@5.0.11) + '@astrojs/rss': + specifier: ^4.0.2 + version: 4.0.2 '@astrojs/tailwind': specifier: ^5.1.0 version: 5.1.0(astro@4.0.8)(tailwindcss@3.4.0) @@ -890,6 +893,13 @@ packages: - vite dev: false + /@astrojs/rss@4.0.2: + resolution: {integrity: sha512-Hb9GKAyvsn5EUjZtB6SniesBScMQe7SQinEHLY5EFa74QEvgcWaXTmA0Mb0P3vqDSN3d/NTYbGivprrSAawfnA==} + dependencies: + fast-xml-parser: 4.3.3 + kleur: 4.1.5 + dev: false + /@astrojs/tailwind@5.1.0(astro@4.0.8)(tailwindcss@3.4.0): resolution: {integrity: sha512-BJoCDKuWhU9FT2qYg+fr6Nfb3qP4ShtyjXGHKA/4mHN94z7BGcmauQK23iy+YH5qWvTnhqkd6mQPQ1yTZTe9Ig==} peerDependencies: @@ -7182,6 +7192,13 @@ packages: /fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + /fast-xml-parser@4.3.3: + resolution: {integrity: sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: false + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -12637,6 +12654,10 @@ packages: acorn: 8.11.3 dev: true + /strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + dev: false + /strong-log-transformer@2.1.0: resolution: {integrity: sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==} engines: {node: '>=4'} diff --git a/website/package.json b/website/package.json index 2fa4e8e1..a84527cf 100644 --- a/website/package.json +++ b/website/package.json @@ -16,6 +16,7 @@ "@astro-community/astro-embed-youtube": "^0.4.3", "@astrojs/mdx": "^2.0.3", "@astrojs/react": "^3.0.9", + "@astrojs/rss": "^4.0.2", "@astrojs/tailwind": "^5.1.0", "@docsearch/css": "^3.5.2", "@docsearch/react": "^3.5.2", @@ -23,21 +24,21 @@ "@heroicons/react": "^2.1.1", "@nanostores/persistent": "^0.9.1", "@nanostores/react": "^0.7.1", + "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", "@strudel/csound": "workspace:*", + "@strudel/desktopbridge": "workspace:*", + "@strudel/hydra": "workspace:*", "@strudel/midi": "workspace:*", "@strudel/mini": "workspace:*", "@strudel/osc": "workspace:*", + "@strudel/repl": "workspace:*", "@strudel/serial": "workspace:*", "@strudel/soundfonts": "workspace:*", "@strudel/tonal": "workspace:*", "@strudel/transpiler": "workspace:*", "@strudel/webaudio": "workspace:*", "@strudel/xen": "workspace:*", - "@strudel/codemirror": "workspace:*", - "@strudel/desktopbridge": "workspace:*", - "@strudel/hydra": "workspace:*", - "@strudel/repl": "workspace:*", "@supabase/supabase-js": "^2.39.1", "@tailwindcss/forms": "^0.5.7", "@tailwindcss/typography": "^0.5.10", diff --git a/website/src/components/BlogPost.astro b/website/src/components/BlogPost.astro index b5b82680..6117d3ce 100644 --- a/website/src/components/BlogPost.astro +++ b/website/src/components/BlogPost.astro @@ -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'; ---
diff --git a/website/src/components/HeadCommon.astro b/website/src/components/HeadCommon.astro index cb3e605a..a352a2c7 100644 --- a/website/src/components/HeadCommon.astro +++ b/website/src/components/HeadCommon.astro @@ -10,7 +10,7 @@ const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL - + - + diff --git a/website/src/components/HeadSEO.astro b/website/src/components/HeadSEO.astro index 556b50a7..413668c1 100644 --- a/website/src/components/HeadSEO.astro +++ b/website/src/components/HeadSEO.astro @@ -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; - - - - - - - - diff --git a/website/src/docs/Icon.jsx b/website/src/docs/Icon.jsx index 64d5f88a..48b05d87 100644 --- a/website/src/docs/Icon.jsx +++ b/website/src/docs/Icon.jsx @@ -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 ( + + + + ); + } return ( { @@ -31,6 +46,13 @@ export function Icon({ type }) { clipRule="evenodd" /> ), + skip: ( + + ), }[type] } diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 33dadedf..10eff483 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -1,8 +1,8 @@ import { useState, useRef, useCallback, useMemo, useEffect } from 'react'; import { Icon } from './Icon'; -import { silence, getPunchcardPainter, noteToMidi } from '@strudel/core'; +import { silence, getPunchcardPainter, noteToMidi, _mod } from '@strudel/core'; import { transpiler } from '@strudel/transpiler'; -import { getAudioContext, webaudioOutput } from '@strudel/webaudio'; +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
{code}
; } @@ -119,6 +130,28 @@ export function MiniRepl({
+ {tunes && ( +
+ + +
+ )}
)}
diff --git a/website/src/examples.mjs b/website/src/examples.mjs new file mode 100644 index 00000000..d752e711 --- /dev/null +++ b/website/src/examples.mjs @@ -0,0 +1,81 @@ +export const examples = [ + `// "coastline" @by eddyflux +await samples('github:eddyflux/crate') +setcps(.75) +let chords = chord("/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("?").delay(".8:.1:.8").room(2).slow(8).cut(2), + ).reset("".late(2))`, + `// "acidic tooth" @by eddyflux + setcps(1) + stack( + note("[/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') + )`, +]; diff --git a/website/src/pages/blog.astro b/website/src/pages/blog.astro index 80270353..f735e7ba 100644 --- a/website/src/pages/blog.astro +++ b/website/src/pages/blog.astro @@ -32,7 +32,18 @@ const posts = (await getCollection('blog')).sort((a, b) => compareDesc(a.data.da - {posts.map((post) => )} +
+

Strudel Blog

+

+ 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 this rss link +

+
+
+ {posts.map((post) => )} +