mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-22 11:08:35 +00:00
lazy midi init + remove useWebMidi hook
This commit is contained in:
parent
22711dd7be
commit
b5c51e231b
@ -5,13 +5,18 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as _WebMidi from 'webmidi';
|
import * as _WebMidi from 'webmidi';
|
||||||
import { Pattern, isPattern, isNote, getPlayableNoteValue } from '@strudel.cycles/core';
|
import { Pattern, isPattern, isNote, getPlayableNoteValue, logger } from '@strudel.cycles/core';
|
||||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||||
|
|
||||||
// if you use WebMidi from outside of this package, make sure to import that instance:
|
// if you use WebMidi from outside of this package, make sure to import that instance:
|
||||||
export const { WebMidi } = _WebMidi;
|
export const { WebMidi } = _WebMidi;
|
||||||
|
|
||||||
export function enableWebMidi() {
|
export function enableWebMidi(options = {}) {
|
||||||
|
const { onReady, onConnected, onDisconnected } = options;
|
||||||
|
|
||||||
|
if (typeof navigator.requestMIDIAccess !== 'function') {
|
||||||
|
throw new Error('Your Browser does not support WebMIDI.');
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (WebMidi.enabled) {
|
if (WebMidi.enabled) {
|
||||||
// if already enabled, just resolve WebMidi
|
// if already enabled, just resolve WebMidi
|
||||||
@ -22,6 +27,14 @@ export function enableWebMidi() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
WebMidi.addListener('connected', (e) => {
|
||||||
|
onConnected?.(WebMidi);
|
||||||
|
});
|
||||||
|
// Reacting when a device becomes unavailable
|
||||||
|
WebMidi.addListener('disconnected', (e) => {
|
||||||
|
onDisconnected?.(WebMidi, e);
|
||||||
|
});
|
||||||
|
onReady?.(WebMidi);
|
||||||
resolve(WebMidi);
|
resolve(WebMidi);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -30,7 +43,21 @@ export function enableWebMidi() {
|
|||||||
const outputByName = (name) => WebMidi.getOutputByName(name);
|
const outputByName = (name) => WebMidi.getOutputByName(name);
|
||||||
|
|
||||||
// Pattern.prototype.midi = function (output: string | number, channel = 1) {
|
// Pattern.prototype.midi = function (output: string | number, channel = 1) {
|
||||||
Pattern.prototype.midi = function (output, channel = 1) {
|
Pattern.prototype.midi = async function (output, channel = 1) {
|
||||||
|
await enableWebMidi({
|
||||||
|
onConnected: ({ outputs }) =>
|
||||||
|
logger(`Midi device connected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`),
|
||||||
|
onDisconnected: ({ outputs }) =>
|
||||||
|
logger(`Midi device disconnected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`),
|
||||||
|
onReady: ({ outputs }) => {
|
||||||
|
const chosenOutput = output ?? outputs[0];
|
||||||
|
const otherOutputs = outputs
|
||||||
|
.filter((o) => o.name !== chosenOutput.name)
|
||||||
|
.map((o) => `'${o.name}'`)
|
||||||
|
.join(' | ');
|
||||||
|
logger(`Midi connected! Using "${chosenOutput.name}". ${otherOutputs ? `Also available: ${otherOutputs}` : ''}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
if (isPattern(output?.constructor?.name)) {
|
if (isPattern(output?.constructor?.name)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
|
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
|
||||||
@ -75,7 +102,7 @@ Pattern.prototype.midi = function (output, channel = 1) {
|
|||||||
device.playNote(note, channel, {
|
device.playNote(note, channel, {
|
||||||
time,
|
time,
|
||||||
duration: hap.duration.valueOf() * 1000 - 5,
|
duration: hap.duration.valueOf() * 1000 - 5,
|
||||||
velocity, // TODO: "OutputChannel.js:942 The 'velocity' option is deprecated. Use 'attack' instead."?
|
attack: velocity,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
return hap.setContext({ ...hap.context, onTrigger });
|
return hap.setContext({ ...hap.context, onTrigger });
|
||||||
|
|||||||
40
packages/react/dist/index.cjs.js
vendored
40
packages/react/dist/index.cjs.js
vendored
File diff suppressed because one or more lines are too long
1710
packages/react/dist/index.es.js
vendored
1710
packages/react/dist/index.es.js
vendored
File diff suppressed because one or more lines are too long
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
useWebMidi.js - <short description TODO>
|
|
||||||
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/src/useWebMidi.js>
|
|
||||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { enableWebMidi, WebMidi } from '@strudel.cycles/midi';
|
|
||||||
|
|
||||||
export function useWebMidi(props) {
|
|
||||||
const { ready, connected, disconnected } = props;
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [outputs, setOutputs] = useState(WebMidi?.outputs || []);
|
|
||||||
useEffect(() => {
|
|
||||||
if (typeof navigator.requestMIDIAccess === 'function') {
|
|
||||||
enableWebMidi()
|
|
||||||
.then(() => {
|
|
||||||
// Reacting when a new device becomes available
|
|
||||||
WebMidi.addListener('connected', (e) => {
|
|
||||||
setOutputs([...WebMidi.outputs]);
|
|
||||||
connected?.(WebMidi, e);
|
|
||||||
});
|
|
||||||
// Reacting when a device becomes unavailable
|
|
||||||
WebMidi.addListener('disconnected', (e) => {
|
|
||||||
setOutputs([...WebMidi.outputs]);
|
|
||||||
disconnected?.(WebMidi, e);
|
|
||||||
});
|
|
||||||
ready?.(WebMidi);
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
//throw new Error("Web Midi could not be enabled...");
|
|
||||||
console.warn('Web Midi could not be enabled..');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.warn(
|
|
||||||
`Your Browser does not support WebMIDI.
|
|
||||||
See https://caniuse.com/?search=web%20midi%20api`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [ready, connected, disconnected, outputs]);
|
|
||||||
const outputByName = (name) => WebMidi.getOutputByName(name);
|
|
||||||
return { loading, outputs, outputByName };
|
|
||||||
}
|
|
||||||
@ -7,4 +7,3 @@ export { default as usePostMessage } from './hooks/usePostMessage';
|
|||||||
export { default as useStrudel } from './hooks/useStrudel';
|
export { default as useStrudel } from './hooks/useStrudel';
|
||||||
export { default as useKeydown } from './hooks/useKeydown';
|
export { default as useKeydown } from './hooks/useKeydown';
|
||||||
export { default as cx } from './cx';
|
export { default as cx } from './cx';
|
||||||
export { useWebMidi } from './hooks/useWebMidi';
|
|
||||||
|
|||||||
@ -5,9 +5,9 @@ This program is free software: you can redistribute it and/or modify it under th
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// import { evaluate } from '@strudel.cycles/eval';
|
// import { evaluate } from '@strudel.cycles/eval';
|
||||||
import { CodeMirror, cx, flash, useHighlighting, useWebMidi } from '@strudel.cycles/react';
|
import { CodeMirror, cx, flash, useHighlighting } from '@strudel.cycles/react';
|
||||||
// import { cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
// import { cleanupDraw, cleanupUi, Tone } from '@strudel.cycles/tone';
|
||||||
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import logo from './logo.svg';
|
import logo from './logo.svg';
|
||||||
import * as tunes from './tunes.mjs';
|
import * as tunes from './tunes.mjs';
|
||||||
@ -153,27 +153,6 @@ function App() {
|
|||||||
// getTime: () => Tone.getTransport().seconds,
|
// getTime: () => Tone.getTransport().seconds,
|
||||||
});
|
});
|
||||||
|
|
||||||
useWebMidi({
|
|
||||||
ready: useCallback(
|
|
||||||
({ outputs }) => {
|
|
||||||
pushLog(`WebMidi ready! Just add .midi(${outputs.map((o) => `'${o.name}'`).join(' | ')}) to the pattern. `);
|
|
||||||
},
|
|
||||||
[pushLog],
|
|
||||||
),
|
|
||||||
connected: useCallback(
|
|
||||||
({ outputs }) => {
|
|
||||||
pushLog(`Midi device connected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
|
||||||
},
|
|
||||||
[pushLog],
|
|
||||||
),
|
|
||||||
disconnected: useCallback(
|
|
||||||
({ outputs }) => {
|
|
||||||
pushLog(`Midi device disconnected! Available: ${outputs.map((o) => `'${o.name}'`).join(', ')}`);
|
|
||||||
},
|
|
||||||
[pushLog],
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col">
|
<div className="min-h-screen flex flex-col">
|
||||||
{!hideHeader && (
|
{!hideHeader && (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user