import { Slides, Slide } from './Slides.jsx';
import { MaxiRepl } from './MaxiRepl';
import ImgTile from './ImgTile';

<Slides>
<Slide>

<h1 className="mb-0">Strudel</h1>

<h2 className="mt-0">Algorithmic Patterns for the Web</h2>

<div className="flex space-x-2 items-center">
  <em>
    "The Analytical Engine weaves algebraic patterns, just as the Jacquard loom weaves flowers and leaves."
    <br />
    “Supposing, for instance, that the fundamental relations of pitched sounds in the science of harmony and of musical
    composition were susceptible of such expression and adaptations, the [Analytical] Engine might compose elaborate and
    scientific pieces of music of any degree of complexity or extent." - Ada Lovelace
  </em>
</div>
<img src="./ada.jpg" className="h-[400px] rounded-md" />

</Slide><Slide>

<img src="./codeandmusic.png" className="h-[700px] rounded-md" />

<div className="text-left">

- How to represent and control music with code?
- How to leverage unique features of computing?
- How to break loose from old conventions?

</div>

</Slide><Slide>

## About Me

<div className="text-left">

- 💻 Studied Computer Science and Media, working as a software developer by day
- 🎺 Trumpet Player, studied Jazz and Popular Music
- 🎤 Dilettante Music Producer of all kinds of genres
- 💿 ocassionally publishes music with Lui Mafuta & Puste
- 🔌 Modular Synth & Electronics Lover, Dilettante DJ ..
- 💌 Coding Blog: [loophole-letters.vercel.app](https://loophole-letters.vercel.app/)
- 👀 Loves visualizations

<div className="grid grid-cols-5 gap-4 mt-12">
  <ImgTile height={200} src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Flambdoma.png&w=2048&q=75" />
  {/*   <ImgTile
    height={300}
    src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Fharmonic-spiral.png&w=2048&q=75"
  /> */}
  <ImgTile height={200} src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Fpiano-roll.png&w=2048&q=75" />
  <ImgTile height={200} src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Fcof.png&w=2048&q=75" />
  <ImgTile height={200} src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2F251graph.png&w=2048&q=75" />
  <ImgTile
    height={200}
    src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Fdiy-modular-synth%2Fmidi2cv-styleshot.png&w=2048&q=75"
  />
  {/*   <ImgTile height={200} src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Fcolorpiano.png&w=2048&q=75" /> */}
  {/* 
  <ImgTile
    height={300}
    src="https://loophole-letters.vercel.app/_next/image?url=%2Fimg%2Fvoicing-permutation.png&w=2048&q=75"
  /> */}
</div>

</div>

</Slide><Slide>

## What is Live Coding?

<i className="text-3xl">
  "In live coding performance, performers create time-based works by programming them while these same works are being
  executed." <br />
  Roberts & Wakefield, 2016
</i>
<img src="./hydra-screenshot.png" className="h-[600px] rounded-md" />
<p>
  Screenshot: "Olivia Jack -{' '}
  <a href="https://hydra.ojack.xyz/" target="_blank">
    Hydra
  </a>
  , Live Coding Visuals in the Browser".{' '}
</p>

</Slide><Slide>

## What is Tidal Cycles?

<div className="text-left">

- A very popular software for live coding music
- a powerful pattern language for time-based art
- find out more at <a href="https://tidalcycles.org" target="_blank" className="text-indigo-400">tidalcycles.org</a>

</div>

<img src="./tidal-screenshot2.png" className="h-[500px] rounded-md" />
<p>
  Screenshot:{' '}
  <a href="https://youtu.be/tY8BmTnlUA0?t=594" target="_blank">
    "Yaxu & hellocatfood - LIVE code for Noise Quest"
  </a>
</p>

</Slide><Slide>

## What is Strudel?

<div className="text-left">

- a port of Tidal Cycles to JavaScript
- a browser based editor for zero-install live coding: <a href="https://strudel.tidalcycles.org/" target="_blank" className="text-indigo-400">strudel.tidalcycles.org</a>
- many ways to make sound: Tone.js, Webdirt, OSC, MIDI ...

<div className="hidden">

- "Strudel" = internationally known as delicious pastry, but also "swirl" / "whirlpool" in german
- (a tidal can cause a whirlpool!)

</div>

</div>

<img src="./strudel-screenshot.png" className="h-[500px] rounded-md mb-0" />
<p>
  Screenshot:{' '}
  <a href="https://youtu.be/IcMSocdKwvw?t=330" target="_blank">
    "Algorave 10th Birthday March 2022 - froos"
  </a>
</p>

</Slide><Slide>

## A Taste of Strudel

<MaxiRepl
  canvasHeight={600}
  code={[
    `"<0 2 [4 6](3,4,1) 3*2>"
.scale('D minor')
.color('salmon')
.off(1/4, x=>x.scaleTranspose(2).color('green'))
.off(1/2, x=>x.scaleTranspose(6).color('steelblue'))
.legato(.5)
.echo(2, 1/8, .5)
.tone((await piano()).chain(out()))
.pianoroll()`,
    //
    `// froos - xylophone on the floor
const t = x => x.scaleTranspose("<0 2 4 3>/4").transpose(-2)
const s = x => x.scale(
  cat('C3 minor pentatonic','G3 minor pentatonic')
  .slow(4))
const delay = new FeedbackDelay(1/8, .6).chain(vol(0.1), out());
const chorus = new Chorus(1,2.5,0.5).start();
stack(
  // melody
  "<<10 7> <8 3>>/4".struct("x*3").apply(s)
  .scaleTranspose("<0 3 2> <1 4 3>")
  .superimpose(scaleTranspose(2).early(1/8))
  .apply(t).tone(polysynth().set({
    ...osc('triangle4'),
    ...adsr(0,.08,0)
  })
  .chain(vol(0.2).connect(delay),chorus,out()))
  .mask("<~@3 x>/16".early(1/8)),
  // pad
  "[1,3]/4".scale('G3 minor pentatonic').apply(t)
  .tone(polysynth().set({
    ...osc('square2'),
    ...adsr(0.1,.4,0.8)
  }).chain(vol(0.2),chorus,out())).mask("<~ x>/32"),
  // xylophone
  "c3,g3,c4".struct("<x*2 x>").fast("<1 <2!3 [4 8]>>")
    .apply(s)
    .scaleTranspose("<0 <1 [2 [3 <4 5>]]>>").apply(t)
    .tone(polysynth().set({
    ...osc('sawtooth4'),
    ...adsr(0,.1,0)
  }).chain(vol(0.4).connect(delay),out()))
  .mask("<x@3 ~>/16".early(1/8)),
  // bass
  "c2 [c2 ~]*2".scale('C hirajoshi').apply(t)
  .tone(synth({
    ...osc('sawtooth6'),
    ...adsr(0,.03,.4,.1)
  }).chain(vol(0.4),out())),
  // kick
  "<c1!3 [c1 ~]*2>*2".tone(membrane().chain(vol(0.8),out())),
  // snare
  "~ <c3!7 [c3 c3*2]>".tone(noise().chain(vol(0.8),out())),
  // hihat
  "c3*4".transpose("[-24 0]*2").tone(metal(adsr(0,.02))
  .chain(vol(0.5).connect(delay),out()))
).slow(1)`,
    `// froos - strudel schneckno
samples({
  clubkick: 'clubkick/2.wav',
  sd: ['808sd/SD0010.WAV','808sd/SD0050.WAV'],
  hh: 'hh/000_hh3closedhh.wav',
  clak: 'clak/000_clak1.wav',
  jvbass: ['jvbass/000_01.wav','jvbass/001_02.wav','jvbass/003_04.wav','jvbass/004_05.wav','jvbass/005_06.wav']
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
stack(
  s("<clubkick*2>,[~ <sd!3 sd(3,4,2)>],hh(3,4,2)")
    .n("<0 1 2>").speed(.8),
  n("<0 1*2 4(3,8) 2>").speed(.6)
    .off(1/8, x => x.speed(1.2).degradeBy(.5))
    .s("jvbass")
).reset("<x@7 <x(5,8,3) x(3,8) x(3,4,1)>>")
  .cutoff(perlin.slow(7).range(100,20000))
  .webdirt().slow(3/2)`,
    `stack( // drum pattern by yaxu
  "24*2 [~ 25]".chunk(4, x => x.sub("12").fast(2))
    .tone(membrane().toDestination()),
  "~ x ~ x".every(2, x => x.fast(2)).iter(4)
    .tone(noise().toDestination()),
  "c4*8".add(saw.mul(12).slow(1.5)).degrade(0.05)
    .tone(metal().set(adsr(0,.06,0,0)).chain(vol(0.5),out()))
)`,
  ]}
/>

</Slide><Slide>

## How it started

![strudel port quote](https://loophole-letters.vercel.app/img/strudel-port.png)

<div className="text-left">

- Started by Alex McLean, based on a port of a port of a rewrite of Tidal
- I implemented the REPL
- Help from Tidal Community Members
- Started with Tone.js, then added Web MIDI, OSC and Web Audio API outputs
- Inspired by: Tidal Vortex, Gibber, Estuary, Hydra, WAGS and Feedforward.

</div>

</Slide><Slide>

## REPL = Read Eval Play Loop

<div className="text-left">

1. Read: User code is parsed, syntax sugar is dissolved
2. Eval: Transformed code is evaluated, creating a **Pattern**
3. Play: The **Pattern** is queried for events, which trigger sounds and effects
4. Loop: The new output is fed back into the ears of the human, informing the next move

</div>

<img src="./repl.png" className="m-0" />

</Slide><Slide>

## Mini Notation

\* \[\] ~ , / <\> ! @ \(\)

<div className="text-left">

- Embedded Domain Specific Language
- PEG.js grammar based on krill by Marc Resibois

</div>

<MaxiRepl
  code={[
    '"c3".pianoroll({ fold:1 })',
    `// mini notation also works with samples
s("bd,[~ sd],hh*4").webdirt()`,
    `\`[[e5 [b4 c5] d5 [c5 b4]]
[a4 [a4 c5] e5 [d5 c5]]
[b4 [~ c5] d5 e5]
[c5 a4 a4 ~]
[[~ d5] [~ f5] a5 [g5 f5]]
[e5 [~ c5] e5 [d5 c5]]
[b4 [b4 c5] d5 e5]
[c5 a4 a4 ~]],
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[a2 a3 a2 a3 a2 a3 b1 c2]
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]\`.slow(16)
.pianoroll({ fold:1, cycles:16 })`,
    `stack(
  // melody
  \`<
  [e5 ~] [[d5@2 c5] [~@2 e5]] ~ [~ [c5@2 d5]] [e5 e5] [d5 c5] [e5 f5] [g5 a5]
  [~ c5] [c5 d5] [e5 [c5@2 c5]] [~ c5] [f5 e5] [c5 d5] [~ g6] [g6 ~]
  [e5 ~] [[d5@2 c5] [~@2 e5]] ~ [~ [c5@2 d5]] [e5 e5] [d5 c5] [a5 g5] [c6 [e5@2 d5]]
  [~ c5] [c5 d5] [e5 [c5@2 c5]] [~ c5] [f5 e5] [c5 d5] [~ [g6@2 ~] ~@2] [g5 ~] 
  [~ a5] [b5 c6] [b5@2 ~@2 g5] ~
  [f5 ~] [[g5@2 f5] ~] [[e5 ~] [f5 ~]] [[f#5 ~] [g5 ~]]
  [~ a5] [b5 c6] [b5@2 ~@2 g5] ~
  [eb6 d6] [~ c6] ~!2
  >\`
  .legato(.95),
  // sub melody
  \`<
  [~ g4]!2 [~ ab4]!2 [~ a4]!2 [~ bb4]!2 
  [~ a4]!2 [~ g4]!2 [d4 e4] [f4 gb4] ~!2
  [~ g4]!2 [~ ab4]!2 [~ a4]!2 [~ bb4]!2 
  [~ a4]!2 [~ g4]!2 [d4 e4] [f4 gb4] ~!2
  [~ c5]!4 [~ a4]!2 [[c4 ~] [d4 ~]] [[eb4 ~] [e4 ~]]
  [~ c5]!4 [~ eb5]!2 [g4*2 [f4 ~]] [[e4 ~] [d4 ~]]
  >\`,
  // bass
  \`<
  c3!7 a3 f3!2
  e3!2 ~!4
  c3!7 a3 f3!2
  e3!2 ~!4
  f3!2 e3!2 d3!2 ~!2
  f3!2 e3!2 ab3!2 ~!2
  >\`
  .legato(.5)
).fast(2).pianoroll({fold:1,cycles:8,overscan:8})`,
  ]}
/>

</Slide><Slide>

## Pattern Factories

seq, cat, stack

<MaxiRepl
  code={[
    `seq(c3,e3,g3) // "c3 e3 g3"
    .tone((await piano()).toDestination())
    .pianoroll()`,
    `cat(c3,e3,g3) // "<c3 e3 g3>"
    .tone((await piano()).toDestination())
    .pianoroll()`,
    `stack(c3,e3,g3) // "c3,e3,g3"
    .tone((await piano()).toDestination())
    .pianoroll()`,
  ]}
/>

</Slide><Slide>

## Time Control

fast / slow, early / late, rev

<MaxiRepl
  code={[
    `"[c3 e3 g3]".fast(2) // "[c3 e3 g3]*2"
    .tone((await piano()).toDestination())
    .pianoroll()`,
    `"[c3 e3 g3]".slow(2) // "[c3 e3 g3]/2"
    .tone((await piano()).toDestination())
    .pianoroll()`,
    `stack("c3 e3","<g3 a3 bb3 a3>".early(.16)).late(.16)
    .tone((await piano()).toDestination())
    .pianoroll()`,
    `"c3 e3 g3 bb3".rev()
    .tone((await piano()).toDestination())
    .pianoroll()`,
  ]}
/>

</Slide><Slide>

## Arithmetic & Patternified Params

add, sub, mul, div

<MaxiRepl
  code={[
    `"30,42 45 49 54".add(0)
    .tone((await piano()).toDestination())
    .pianoroll({autorange:0,minMidi:30,maxMidi:56})`,
    `"30,42 45 49 54".add("<0 1 2 1>/2")
    .tone((await piano()).toDestination())
    .pianoroll({fold:1,cycles:8})`,
  ]}
/>

</Slide><Slide>

## Scales

<MaxiRepl
  code={[
    `"-7,0 2 4 7".add("<0 1 2 1>")
    .scale('F#2 major')
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1})`,
    `"-7, 0 2 4 7".add("<0 1 2 1>")
    .scale(cat('F#2 major','A2 major').slow(8))
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1,cycles:16,fold:1,inactive:'orange'})`,
  ]}
/>

</Slide><Slide>

## Articulation

velocity, legato

<MaxiRepl
  code={[
    `"-7, 0 2 4 7".add("<0 1 2 1>")
    .scale(cat('F#2 major','A2 major').slow(8))
    .legato(.25)
    .velocity(.5) 
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1,cycles:16,fold:1,inactive:'orange'})`,
    `"-7, 0 2 4 7".add("<0 1 2 1>")
    .scale(cat('F#2 major','A2 major').slow(8))
    .legato("<0.25 2>/8")
    .velocity("<1 .8>/8")
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1,cycles:16,fold:1,inactive:'orange'})`,
  ]}
/>

</Slide><Slide>

## Continuous Signals / Randomness

sine / saw / tri / square / rand / perlin, range, segment

<MaxiRepl
  code={[
    `sine
  .range(42,54).slow(4).segment(8)
  .pianoroll({ minMidi:42, maxMidi: 54, autorange:0,
              cycles:8,fold:0,inactive:'orange'})`,
  ]}
/>

</Slide><Slide>

## Humanization

"articulate randomness"

<MaxiRepl
  code={[
    `"-7, 0 2 4 7".add("<0 1 2 1>")
    .scale(cat('F#2 major','A2 major').slow(8))
    .legato(perlin.range(.5,2))
    .velocity("<0.4 .3 .7>*3")
    .late(perlin.range(0,.05))
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1,cycles:16,fold:1,inactive:'orange'})`,
  ]}
/>

</Slide><Slide>

## Shuffling

iter

<MaxiRepl
  code={[
    `"c3 e3 g3 c4".iter(4)
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1,cycles:8,fold:1,inactive:'indigo'})`,
    `stack("-7", "0 2 4 7".iter(4)).add("<0 1 2 1>")
    .scale(cat('F#2 major','A2 major').slow(8))
    .legato(perlin.range(.5,2))
    .velocity(rand.range(.6,.9))
    .tone((await piano()).toDestination())
    .pianoroll({autorange:1,cycles:16,fold:1,inactive:'indigo'})`,
  ]}
/>

</Slide><Slide>

## Superposition

superimpose, off, echo, echoWith

<MaxiRepl
  code={[
    `"0 2 4 7"
  .superimpose(x=>x.add(7).color('green'))
  .scale('C major').slow(2)
  .tone((await piano()).toDestination())
  .pianoroll({autorange:1,cycles:8,fold:1,inactive:'indigo'})`,
    `"0 2 4 7"
  .off(.17, x=>x.add(12).color('green'))
  .scale('C major').slow(2)
  .tone((await piano()).toDestination())
  .pianoroll({autorange:1,cycles:8,fold:1,inactive:'indigo'})`,
    `"0 2 4 7"
  .echo(6,.125,.8)
  .scale('C major').slow(2)
  .tone((await piano()).toDestination())
  .pianoroll({autorange:1,cycles:8,fold:1,inactive:'indigo'})`,
  ]}
/>

</Slide><Slide>

## Phasing

Oscillator Detune in slow motion

<MaxiRepl
  code={[
    `// Steve Reich - Piano Phase
"E4 F#4 B4 C#5 D5 F#4 E4 C#5 B4 F#4 D5 C#5".color('#00ff0050')
  .superimpose(x=>x.fast(1.01).color('#ff00ff50'))
  .slow(2)
  .tone((await piano()).toDestination())
  .pianoroll({fold:1,vertical:0,cycles:8})`,
    `// Steve Reich - Clapping Music
s("cp").struct("x!3 ~ x!2 ~ x ~ x!2 ~").speed(.8)
  .jux(x=>x.speed(1.2)
    .late("<0 1 2 3 4 5 6 7 8 9 10 11>/8".div(12))
  ).slow(2).webdirt()`,
  ]}
/>

</Slide><Slide>

## Continuous Signals

<MaxiRepl
  code={[
    `sine.range(50,62).segment(32).slow(4)
  .pianoroll({minMidi:48,maxMidi:62})`,
    `sine.range(42,50).color('#00ff0050')
  .superimpose(x=>x.fast(3/2).color('#ff00ff50'))
  .fast(1)
  .segment(64)
  .pianoroll({fold:0,vertical:0,cycles:2})
// strudel disable-highlighting`,
  ]}
/>

<MaxiRepl
  code={`s("hh*8").speed(sine.range(.5,2).slow(4))
  .webdirt()`}
/>

</Slide><Slide>

## Chords and Scales

<MaxiRepl
code={[`// froos - giant steps, chiptune version
stack(
  // melody
  seq(
    "[F#5 D5] [B4 G4] Bb4 [B4 A4]",
    "[D5 Bb4] [G4 Eb4] F#4 [G4 F4]",
    "Bb4 [B4 A4] D5 [D#5 C#5]",
    "F#5 [G5 F5] Bb5 [F#5 [F#5 ~@3]]",
  ),
  // chords
  seq(
    "[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]",
    "[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]",
    "Eb^7 [Am7 D7] G^7 [C#m7 F#7]",
    "B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]"
  )
  .struct("~ [x ~]".fast(4*8))
  .voicings(['E3', 'G4']),
  // bass
  seq(
    "[B2 D2] [G2 D2] [Eb2 Bb2] [A2 D2]",
    "[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]",
    "[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]",
    "[B2 F#2] [F2 Bb2] [Eb2 Bb2] [C#2 F#2]"
  )
  .struct("x ~".fast(4*8))
).slow(25)`]}/>
  
</Slide>
</Slides>
