/ proof-of-concept / midiboot
Sign in
proof-of-concept / midiboot
midiboot / src / components / DeviceBar.tsx
53 lines · 1.8 KBBlameHistoryRaw
1import { useSyncExternalStore } from 'react';
2import { midiManager } from '../input/webmidi.ts';
3import { keyboardPiano } from '../input/keyboardPiano.ts';
5function useMidiState() {
6 return useSyncExternalStore(
7 (cb) => midiManager.subscribeState(cb),
8 () => midiManager.getState(),
9 );
12function useKbdState(): number {
13 // encodes enabled+octave so the snapshot is a stable primitive
14 return useSyncExternalStore(
15 (cb) => keyboardPiano.subscribeState(cb),
16 () => (keyboardPiano.enabled ? keyboardPiano.octave : -1),
17 );
20export function DeviceBar() {
21 const midi = useMidiState();
22 const kbd = useKbdState();
23 return (
24 <div className="devicebar">
25 {midi.supported ? (
26 midi.requested && !midi.error ? (
27 <>
28 <span className={`dot ${midi.inputs.length > 0 ? 'ok' : ''}`} title={midi.inputs.length > 0 ? 'MIDI inputs connected' : 'no MIDI inputs'} />
29 <select value={midi.selectedId} onChange={(e) => midiManager.select(e.target.value)} title="MIDI input device">
30 <option value="all">All MIDI inputs ({midi.inputs.length})</option>
31 {midi.inputs.map((i) => (
32 <option key={i.id} value={i.id}>{i.name}</option>
33 ))}
34 </select>
35 </>
36 ) : midi.error ? (
37 <span className="err" title={midi.error}>MIDI unavailable</span>
38 ) : (
39 <button onClick={() => void midiManager.request()}>Enable MIDI</button>
40 )
41 ) : (
42 <span className="err">No Web MIDI in this browser</span>
43 )}
44 <button
45 className={kbd >= 0 ? 'toggled' : ''}
46 onClick={() => keyboardPiano.setEnabled(kbd < 0)}
47 title="Play with the typing keyboard (a..' row; z/x shift octave)"
48 >
49 {kbd >= 0 ? `Keys: C${kbd}` : 'Keys: off'}
50 </button>
51 </div>
52 );
↑↓ move↵openescclose