add events to audio slide

This commit is contained in:
Felix Roos
2023-04-06 12:34:34 +02:00
parent b016d115f5
commit d77725cf1d
6 changed files with 101 additions and 3 deletions
+46
View File
@@ -0,0 +1,46 @@
import { useState, useCallback } from 'react';
import { atomone } from '@uiw/codemirror-themes-all';
import { CodeMirror, flash, useKeydown } from '@strudel.cycles/react';
import { initAudioOnFirstClick, webaudioOutput } from '@strudel.cycles/webaudio';
import { TimeSpan } from '@strudel.cycles/core';
const initAudio = initAudioOnFirstClick();
function EventEditor({ code: initialCode }) {
const [code, setCode] = useState(initialCode);
const [view, setView] = useState();
useKeydown(
useCallback(
async (e) => {
if (view?.hasFocus) {
if (e.ctrlKey || e.altKey) {
if (e.code === 'Enter') {
/* if (getAudioContext().state !== 'running') {
alert('please click play to initialize the audio. you can use shortcuts after that!');
return;
} */
e.preventDefault();
flash(view);
let json = JSON.parse(code);
if (!Array.isArray(json)) {
json = [json];
}
await initAudio;
json.forEach((value) => {
const hap = new Hap(new TimeSpan(0, 1), new TimeSpan(0, 1), value);
webaudioOutput(hap, 0.1, 1, 1);
});
} else if (e.key === '.') {
stop();
e.preventDefault();
}
}
}
},
[code, view],
),
);
return <CodeMirror value={code} onChange={setCode} theme={atomone} onViewChanged={(v) => setView(v)} fontSize={32} />;
}
export default EventEditor;
+45
View File
@@ -0,0 +1,45 @@
import EventEditor from './EventEditor.jsx';
import { useState } from 'react';
const snippets = [
[`{ "s": "cp" }`, 'Samples'],
[`{ "s": "sawtooth", "note": "e3" }`, 'Oscillators'],
[
`[
{ "s": "gm_epiano2", "note": "c4" },
{ "s": "gm_epiano2", "note": "eb4" },
{ "s": "gm_epiano2", "note": "g4" }
]`,
'Soundfonts',
],
[
`{
"s": "toys",
"cutoff": 2000,
"delay": 0.5,
"crush": 4
}`,
'Effects',
],
];
function EventsDemo() {
const [step, setStep] = useState(1);
return (
<div className="not-prose">
{snippets.slice(0, step).map(([code, label], i) => {
const isActive = i === step - 1;
return (
<div key={i} className={`border-l-4 pl-8 border-gray-500 py-4 ${isActive ? 'p-4 border-yellow-500' : ''}`}>
<h3 className="pb-4">{label}</h3>
<EventEditor code={code} />
</div>
);
})}
{step < snippets.length && <button onClick={() => setStep((r) => r + 1)}>next</button>}
</div>
);
}
export default EventsDemo;
+1 -1
View File
@@ -14,7 +14,7 @@ function MiniComparison() {
const [step, setStep] = useState(1);
return (
<>
<h1>Mini Notation vs Regular JS</h1>
<h1>From Mini Notation to JavaScript</h1>
<div className="not-prose">
{snippets.slice(0, step).map(([a, b, label], i) => {
const isActive = i === step - 1;
+1 -1
View File
@@ -16,7 +16,7 @@ const loadedMDXFiles = await Promise.all(
}),
);
const order = ['01', '02-0', '02-1', '02-2', 'mini-comparison', 'patterns-events', '04'];
const order = ['01', '02-0', '02-1', '02-2', 'mini-comparison', 'patterns-events', 'events-audio', '04'];
const slideEntries = order.map((name) => loadedMDXFiles.find(([file]) => file === name));
@@ -0,0 +1,7 @@
import { SlideRepl } from '../SlideRepl.jsx';
import Highlight from '../Highlight.jsx';
import EventsDemo from '../EventsDemo.jsx';
# From Values to Sound
<EventsDemo />
@@ -5,7 +5,7 @@ import Highlight from '../Highlight.jsx';
<SlideRepl tune={`sound("bd [hh sd]").log()`} hideHeader punchcard />
query = get events within time arc:
query = get events within time arc. event = time + value
<Highlight code={`sound("bd [hh sd]").queryArc(0, 1)`} language="javascript" />