mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 13:48:40 +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 { Pattern, isPattern, isNote, getPlayableNoteValue } from '@strudel.cycles/core';
|
||||
import { Pattern, isPattern, isNote, getPlayableNoteValue, logger } from '@strudel.cycles/core';
|
||||
import { getAudioContext } from '@strudel.cycles/webaudio';
|
||||
|
||||
// if you use WebMidi from outside of this package, make sure to import that instance:
|
||||
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) => {
|
||||
if (WebMidi.enabled) {
|
||||
// if already enabled, just resolve WebMidi
|
||||
@ -22,6 +27,14 @@ export function enableWebMidi() {
|
||||
if (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);
|
||||
});
|
||||
});
|
||||
@ -30,7 +43,21 @@ export function enableWebMidi() {
|
||||
const outputByName = (name) => WebMidi.getOutputByName(name);
|
||||
|
||||
// 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)) {
|
||||
throw new Error(
|
||||
`.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, {
|
||||
time,
|
||||
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 });
|
||||
|
||||
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 useKeydown } from './hooks/useKeydown';
|
||||
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 { 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 React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import './App.css';
|
||||
import logo from './logo.svg';
|
||||
import * as tunes from './tunes.mjs';
|
||||
@ -153,27 +153,6 @@ function App() {
|
||||
// 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 (
|
||||
<div className="min-h-screen flex flex-col">
|
||||
{!hideHeader && (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user