build
This commit is contained in:
parent
f6c4bdb8a6
commit
c891dda8eb
6 changed files with 128 additions and 1 deletions
|
|
@ -15,6 +15,7 @@
|
||||||
"shift-codegen": "./shift-codegen.js",
|
"shift-codegen": "./shift-codegen.js",
|
||||||
"shift-regexp-acceptor": "./shift-regexp-acceptor.js",
|
"shift-regexp-acceptor": "./shift-regexp-acceptor.js",
|
||||||
"shift-spec": "./shift-spec.js",
|
"shift-spec": "./shift-spec.js",
|
||||||
"tone": "./tone.js"
|
"tone": "./tone.js",
|
||||||
|
"webmidi": "./webmidi.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
37
docs/_snowpack/pkg/webmidi.js
Normal file
37
docs/_snowpack/pkg/webmidi.js
Normal file
File diff suppressed because one or more lines are too long
12
docs/dist/App.js
vendored
12
docs/dist/App.js
vendored
|
|
@ -8,6 +8,7 @@ import * as parser from "./parse.js";
|
||||||
import CodeMirror from "./CodeMirror.js";
|
import CodeMirror from "./CodeMirror.js";
|
||||||
import hot from "../hot.js";
|
import hot from "../hot.js";
|
||||||
import {isNote} from "../_snowpack/pkg/tone.js";
|
import {isNote} from "../_snowpack/pkg/tone.js";
|
||||||
|
import {useWebMidi} from "./midi.js";
|
||||||
const {tetris, tetrisRev, shapeShifted} = tunes;
|
const {tetris, tetrisRev, shapeShifted} = tunes;
|
||||||
const {parse} = parser;
|
const {parse} = parser;
|
||||||
const getHotCode = async () => {
|
const getHotCode = async () => {
|
||||||
|
|
@ -108,6 +109,17 @@ function App() {
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
logBox.current.scrollTop = logBox.current?.scrollHeight;
|
logBox.current.scrollTop = logBox.current?.scrollHeight;
|
||||||
}, [log]);
|
}, [log]);
|
||||||
|
useWebMidi({
|
||||||
|
ready: useCallback(({outputs}) => {
|
||||||
|
pushLog(`WebMidi ready! Just add .midi(${outputs.map((o) => `"${o.name}"`).join(" | ")}) to the pattern. `);
|
||||||
|
}, []),
|
||||||
|
connected: useCallback(({outputs}) => {
|
||||||
|
pushLog(`Midi device connected! Available: ${outputs.map((o) => `"${o.name}"`).join(", ")}`);
|
||||||
|
}, []),
|
||||||
|
disconnected: useCallback(({outputs}) => {
|
||||||
|
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `"${o.name}"`).join(", ")}`);
|
||||||
|
}, [])
|
||||||
|
});
|
||||||
return /* @__PURE__ */ React.createElement("div", {
|
return /* @__PURE__ */ React.createElement("div", {
|
||||||
className: "h-screen bg-slate-900 flex flex-col"
|
className: "h-screen bg-slate-900 flex flex-col"
|
||||||
}, /* @__PURE__ */ React.createElement("header", {
|
}, /* @__PURE__ */ React.createElement("header", {
|
||||||
|
|
|
||||||
75
docs/dist/midi.js
vendored
Normal file
75
docs/dist/midi.js
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import {useEffect, useState} from "../_snowpack/pkg/react.js";
|
||||||
|
import {isNote} from "../_snowpack/pkg/tone.js";
|
||||||
|
import _WebMidi from "../_snowpack/pkg/webmidi.js";
|
||||||
|
import {Pattern as _Pattern} from "../_snowpack/link/strudel.js";
|
||||||
|
import * as Tone from "../_snowpack/pkg/tone.js";
|
||||||
|
const WebMidi = _WebMidi;
|
||||||
|
const Pattern = _Pattern;
|
||||||
|
export default function enableWebMidi() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (WebMidi.enabled) {
|
||||||
|
resolve(WebMidi);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WebMidi.enable((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(WebMidi);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const outputByName = (name) => WebMidi.getOutputByName(name);
|
||||||
|
Pattern.prototype.midi = function(output, channel = 1) {
|
||||||
|
return this.fmap((value) => ({
|
||||||
|
...value,
|
||||||
|
onTrigger: (time, event) => {
|
||||||
|
if (!isNote(value)) {
|
||||||
|
throw new Error("not a note: " + value);
|
||||||
|
}
|
||||||
|
if (!WebMidi.enabled) {
|
||||||
|
throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`);
|
||||||
|
}
|
||||||
|
if (!WebMidi.outputs.length) {
|
||||||
|
throw new Error(`🔌 No MIDI devices found. Connect a device or enable IAC Driver.`);
|
||||||
|
}
|
||||||
|
const device = output ? outputByName(output) : WebMidi.outputs[0];
|
||||||
|
if (!device) {
|
||||||
|
throw new Error(`🔌 MIDI device ${output ? output : ""} not found. Use one of ${WebMidi.outputs.map((o) => `"${o.name}"`).join(" | ")}`);
|
||||||
|
}
|
||||||
|
const timingOffset = WebMidi.time - Tone.context.currentTime * 1e3;
|
||||||
|
time = time * 1e3 + timingOffset;
|
||||||
|
device.playNote(value, channel, {
|
||||||
|
time,
|
||||||
|
duration: event.duration * 1e3,
|
||||||
|
velocity: 0.9
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
export function useWebMidi(props) {
|
||||||
|
const {ready, connected, disconnected} = props;
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [outputs, setOutputs] = useState(WebMidi?.outputs || []);
|
||||||
|
useEffect(() => {
|
||||||
|
enableWebMidi().then(() => {
|
||||||
|
WebMidi.addListener("connected", (e) => {
|
||||||
|
setOutputs([...WebMidi.outputs]);
|
||||||
|
connected?.(WebMidi, e);
|
||||||
|
});
|
||||||
|
WebMidi.addListener("disconnected", (e) => {
|
||||||
|
setOutputs([...WebMidi.outputs]);
|
||||||
|
disconnected?.(WebMidi, e);
|
||||||
|
});
|
||||||
|
ready?.(WebMidi);
|
||||||
|
setLoading(false);
|
||||||
|
}).catch((err) => {
|
||||||
|
if (err) {
|
||||||
|
console.warn("Web Midi could not be enabled..");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [ready, connected, disconnected, outputs]);
|
||||||
|
const outputByName2 = (name) => WebMidi.getOutputByName(name);
|
||||||
|
return {loading, outputs, outputByName: outputByName2};
|
||||||
|
}
|
||||||
1
docs/dist/parse.js
vendored
1
docs/dist/parse.js
vendored
|
|
@ -2,6 +2,7 @@ import * as krill from "../_snowpack/link/repl/krill-parser.js";
|
||||||
import * as strudel from "../_snowpack/link/strudel.js";
|
import * as strudel from "../_snowpack/link/strudel.js";
|
||||||
import {Scale, Note, Interval} from "../_snowpack/pkg/@tonaljs/tonal.js";
|
import {Scale, Note, Interval} from "../_snowpack/pkg/@tonaljs/tonal.js";
|
||||||
import "./tone.js";
|
import "./tone.js";
|
||||||
|
import "./midi.js";
|
||||||
import * as toneStuff from "./tone.js";
|
import * as toneStuff from "./tone.js";
|
||||||
import shapeshifter from "./shapeshifter.js";
|
import shapeshifter from "./shapeshifter.js";
|
||||||
const {pure, stack, slowcat, fastcat, cat, sequence, polymeter, pm, polyrhythm, pr, silence, Fraction} = strudel;
|
const {pure, stack, slowcat, fastcat, cat, sequence, polymeter, pm, polyrhythm, pr, silence, Fraction} = strudel;
|
||||||
|
|
|
||||||
1
docs/dist/tunes.js
vendored
1
docs/dist/tunes.js
vendored
|
|
@ -20,6 +20,7 @@ export const shapeShifted = `stack(
|
||||||
a1, a2, a1, a2, a1, a2, a1, a2,
|
a1, a2, a1, a2, a1, a2, a1, a2,
|
||||||
).rev()
|
).rev()
|
||||||
).slow(16).rev()`;
|
).slow(16).rev()`;
|
||||||
|
export const tetrisMidi = `${shapeShifted}.midi('IAC-Treiber Bus 1')`;
|
||||||
export const tetrisWithFunctions = `stack(sequence(
|
export const tetrisWithFunctions = `stack(sequence(
|
||||||
'e5', sequence('b4', 'c5'), 'd5', sequence('c5', 'b4'),
|
'e5', sequence('b4', 'c5'), 'd5', sequence('c5', 'b4'),
|
||||||
'a4', sequence('a4', 'c5'), 'e5', sequence('d5', 'c5'),
|
'a4', sequence('a4', 'c5'), 'e5', sequence('d5', 'c5'),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue