import { evalScope, controls } from '@strudel.cycles/core'; import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio'; import { useCallback, useState } from 'react'; import CodeMirror, { flash } from '../../../src/components/CodeMirror6'; import useKeydown from '../../../src/hooks/useKeydown.mjs'; import useStrudel from '../../../src/hooks/useStrudel'; import useHighlighting from '../../../src/hooks/useHighlighting'; import './style.css'; // import { prebake } from '../../../../../repl/src/prebake.mjs'; // TODO: only import stuff when play is pressed? evalScope( controls, import('@strudel.cycles/core'), // import('@strudel.cycles/tone'), // import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js import('@strudel.cycles/tonal'), import('@strudel.cycles/mini'), import('@strudel.cycles/xen'), import('@strudel.cycles/webaudio'), import('@strudel.cycles/osc'), import('@strudel.cycles/webdirt'), import('@strudel.cycles/serial'), import('@strudel.cycles/soundfonts'), ); const defaultTune = `samples({ bd: ['bd/BT0AADA.wav','bd/BT0AAD0.wav','bd/BT0A0DA.wav','bd/BT0A0D3.wav','bd/BT0A0D0.wav','bd/BT0A0A7.wav'], sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'], hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'], }, 'github:tidalcycles/Dirt-Samples/master/'); stack( s("bd,[~ ],hh(3,4)") // drums .speed(perlin.range(.7,.9)) // random sample speed variation //.hush() ,"" // bassline .off(1/8,x=>x.add(12).degradeBy(.5)) // random octave jumps .add(perlin.range(0,.5)) // random pitch variation .superimpose(add(.05)) // add second, slightly detuned voice .n() // wrap in "n" .decay(.15).sustain(0) // make each note of equal length .s('sawtooth') // waveform .gain(.4) // turn down .cutoff(sine.slow(7).range(300,5000)) // automate cutoff //.hush() ,">".voicings() // chords .superimpose(x=>x.add(.04)) // add second, slightly detuned voice .add(perlin.range(0,.5)) // random pitch variation .n() // wrap in "n" .s('square') // waveform .gain(.16) // turn down .cutoff(500) // fixed cutoff .attack(1) // slowly fade in //.hush() ,"a4 c5 ".struct("x(5,8)") .superimpose(x=>x.add(.04)) // add second, slightly detuned voice .add(perlin.range(0,.5)) // random pitch variation .n() // wrap in "n" .decay(.1).sustain(0) // make notes short .s('triangle') // waveform .degradeBy(perlin.range(0,.5)) // randomly controlled random removal :) .echoWith(4,.125,(x,n)=>x.gain(.15*1/(n+1))) // echo notes //.hush() ) .fast(2/3)`; // await prebake(); const ctx = getAudioContext(); const getTime = () => ctx.currentTime; function App() { const [code, setCode] = useState(defaultTune); const [view, setView] = useState(); // const [code, setCode] = useState(`"c3".note().slow(2)`); const { scheduler, evaluate, schedulerError, evalError, isDirty, activeCode, pattern } = useStrudel({ code, defaultOutput: webaudioOutput, getTime, }); useHighlighting({ view, pattern, active: !activeCode?.includes('strudel disable-highlighting'), getTime: () => scheduler.phase, }); const error = evalError || schedulerError; useKeydown( useCallback( async (e) => { if (e.ctrlKey || e.altKey) { if (e.code === 'Enter') { e.preventDefault(); flash(view); await evaluate(code); if (e.shiftKey) { panic(); scheduler.stop(); scheduler.start(); } if (!scheduler.started) { scheduler.start(); } } else if (e.code === 'Period') { scheduler.pause(); panic(); e.preventDefault(); } } }, [scheduler, evaluate, view], ), ); return (
{/*