mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 13:48:40 +00:00
strudel web component
This commit is contained in:
parent
a8d0d39055
commit
d4afbc63e2
65
website/src/pages/vanilla/mini.astro
Normal file
65
website/src/pages/vanilla/mini.astro
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
import HeadCommonNew from '../../components/HeadCommonNew.astro';
|
||||
---
|
||||
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<HeadCommonNew />
|
||||
<title>Strudel Vanilla REPL</title>
|
||||
</head>
|
||||
<body class="h-app-height">
|
||||
<h1>vanilli repl</h1>
|
||||
<strudel-editor
|
||||
keybindings="emacs"
|
||||
is-line-numbers-displayed="1"
|
||||
is-active-line-highlighted="1"
|
||||
is-auto-completion-enabled="1"
|
||||
is-pattern-highlighting-enabled="1"
|
||||
is-flash-enabled="1"
|
||||
is-tooltip-enabled="1"
|
||||
is-line-wrapping-enabled="1"
|
||||
theme="teletext"
|
||||
font-family="monospace"
|
||||
font-size="18"
|
||||
code={`// @date 23-11-30
|
||||
// "teigrührgerät" @by froos
|
||||
|
||||
stack(
|
||||
stack(
|
||||
s("bd(<3!3 5>,6)/2").bank('RolandTR707')
|
||||
,
|
||||
s("~ sd:<0 1>").bank('RolandTR707').room("<0 .5>")
|
||||
.lastOf(8, x=>x.segment("12").end(.2).gain(isaw))
|
||||
,
|
||||
s("[tb ~ tb]").bank('RolandTR707')
|
||||
.clip(0).release(.08).room(.2)
|
||||
).off(-1/6, x=>x.speed(.7).gain(.2).degrade())
|
||||
,
|
||||
stack(
|
||||
note("<g1(<3 4>,6) ~!2 [f1?]*2>")
|
||||
.s("sawtooth").lpf(perlin.range(400,1000))
|
||||
.lpa(.1).lpenv(-3).room(.2)
|
||||
.lpq(8).noise(.2)
|
||||
.add(note("0,.1"))
|
||||
,
|
||||
chord("<~ Gm9 ~!2>")
|
||||
.dict('ireal').voicing()
|
||||
.s("sawtooth").vib("2:.1")
|
||||
.lpf(1000).lpa(.1).lpenv(-4)
|
||||
.room(.5)
|
||||
,
|
||||
n(run(3)).chord("<Gm9 Gm11>/8")
|
||||
.dict('ireal-ext')
|
||||
.off(1/2, add(n(4)))
|
||||
.voicing()
|
||||
.clip(.1).release(.05)
|
||||
.s("sine").jux(rev)
|
||||
.sometimesBy(sine.slow(16), add(note(12)))
|
||||
.room(.75)
|
||||
.lpf(sine.range(200,2000).slow(16))
|
||||
.gain(saw.slow(4).div(2))
|
||||
).add(note(perlin.range(0,.5)))
|
||||
)`}></strudel-editor>
|
||||
<script src="../../repl/vanilla/strudel-editor.mjs"></script>
|
||||
</body>
|
||||
</html>
|
||||
132
website/src/repl/vanilla/strudel-editor.mjs
Normal file
132
website/src/repl/vanilla/strudel-editor.mjs
Normal file
@ -0,0 +1,132 @@
|
||||
import { getDrawContext, silence, controls, evalScope, hash2code, code2hash } from '@strudel.cycles/core';
|
||||
import { StrudelMirror } from '@strudel/codemirror';
|
||||
import { transpiler } from '@strudel.cycles/transpiler';
|
||||
import { registerSoundfonts } from '@strudel.cycles/soundfonts';
|
||||
import {
|
||||
getAudioContext,
|
||||
webaudioOutput,
|
||||
registerSynthSounds,
|
||||
registerZZFXSounds,
|
||||
samples,
|
||||
} from '@strudel.cycles/webaudio';
|
||||
|
||||
function camelToKebab(camelCaseString) {
|
||||
return camelCaseString.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
||||
}
|
||||
function kebabToCamel(kebabCaseString) {
|
||||
return kebabCaseString.replace(/-([a-z])/g, function (match, group) {
|
||||
return group.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
const initialSettings = {
|
||||
keybindings: 'strudelTheme',
|
||||
isLineNumbersDisplayed: true,
|
||||
isActiveLineHighlighted: true,
|
||||
isAutoCompletionEnabled: false,
|
||||
isPatternHighlightingEnabled: true,
|
||||
isFlashEnabled: true,
|
||||
isTooltipEnabled: false,
|
||||
isLineWrappingEnabled: false,
|
||||
theme: 'teletext',
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 18,
|
||||
};
|
||||
const settingAttributes = Object.keys(initialSettings).map(camelToKebab);
|
||||
const parseAttribute = (name, value) => {
|
||||
const camel = kebabToCamel(name);
|
||||
const type = typeof initialSettings[camel];
|
||||
// console.log('type', type, name);
|
||||
if (type === 'boolean') {
|
||||
return ['1', 'true'].includes(value);
|
||||
}
|
||||
if (type === 'number') {
|
||||
return Number(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
// console.log('attributes', settingAttributes);
|
||||
|
||||
class StrudelEditor extends HTMLElement {
|
||||
static observedAttributes = ['code', ...settingAttributes];
|
||||
settings = initialSettings;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (name === 'code') {
|
||||
this.code = newValue;
|
||||
this.editor?.setCode(initialCode);
|
||||
} else if (settingAttributes.includes(name)) {
|
||||
const camel = kebabToCamel(name);
|
||||
this.settings[camel] = parseAttribute(name, newValue);
|
||||
// console.log('name', name, newValue, camel, this.settings[camel]);
|
||||
this.editor?.updateSettings(this.settings);
|
||||
}
|
||||
}
|
||||
connectedCallback() {
|
||||
const drawContext = getDrawContext();
|
||||
const drawTime = [-2, 2];
|
||||
this.container = document.createElement('div');
|
||||
this.appendChild(this.container);
|
||||
this.editor = new StrudelMirror({
|
||||
defaultOutput: webaudioOutput,
|
||||
getTime: () => getAudioContext().currentTime,
|
||||
transpiler,
|
||||
root: this.container,
|
||||
initialCode: '// LOADING',
|
||||
pattern: silence,
|
||||
settings: this.settings,
|
||||
drawTime,
|
||||
onDraw: (haps, time, frame, painters) => {
|
||||
painters.length && drawContext.clearRect(0, 0, drawContext.canvas.width * 2, drawContext.canvas.height * 2);
|
||||
painters?.forEach((painter) => {
|
||||
// ctx time haps drawTime paintOptions
|
||||
painter(drawContext, time, haps, drawTime, { clear: false });
|
||||
});
|
||||
},
|
||||
prebake: async () => {
|
||||
// populate scope / lazy load modules
|
||||
const modulesLoading = evalScope(
|
||||
import('@strudel.cycles/core'),
|
||||
import('@strudel.cycles/tonal'),
|
||||
import('@strudel.cycles/mini'),
|
||||
import('@strudel.cycles/webaudio'),
|
||||
import('@strudel/codemirror'),
|
||||
import('@strudel/hydra'),
|
||||
import('@strudel.cycles/soundfonts'),
|
||||
// import('@strudel.cycles/xen'),
|
||||
// import('@strudel.cycles/serial'),
|
||||
// import('@strudel.cycles/csound'),
|
||||
/* import('@strudel.cycles/midi'), */
|
||||
// import('@strudel.cycles/osc'),
|
||||
controls, // sadly, this cannot be exported from core directly (yet)
|
||||
);
|
||||
// load samples
|
||||
const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main/';
|
||||
await Promise.all([
|
||||
modulesLoading,
|
||||
registerSynthSounds(),
|
||||
registerZZFXSounds(),
|
||||
registerSoundfonts(),
|
||||
samples(`${ds}/tidal-drum-machines.json`),
|
||||
samples(`${ds}/piano.json`),
|
||||
samples(`${ds}/Dirt-Samples.json`),
|
||||
samples(`${ds}/EmuSP12.json`),
|
||||
samples(`${ds}/vcsl.json`),
|
||||
]);
|
||||
},
|
||||
afterEval: ({ code }) => {
|
||||
window.location.hash = '#' + code2hash(code);
|
||||
},
|
||||
});
|
||||
// init settings
|
||||
this.editor.updateSettings(this.settings);
|
||||
this.editor.setCode(this.code);
|
||||
// settingsMap.listen((settings, key) => editor.changeSetting(key, settings[key]));
|
||||
// onEvent('strudel-toggle-play', () => this.editor.toggle());
|
||||
}
|
||||
// Element functionality written in here
|
||||
}
|
||||
|
||||
customElements.define('strudel-editor', StrudelEditor);
|
||||
Loading…
x
Reference in New Issue
Block a user