@tremolo-ui/react 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +42 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/hooks/useMIDIAccess.ts +20 -27
- package/src/hooks/useMIDIInput.ts +10 -37
- package/src/hooks/useMIDIMessage.ts +4 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tremolo-ui/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React component library for audio app",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"react-dom": "^18 || ^19"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@tremolo-ui/
|
|
45
|
+
"@tremolo-ui/dom": "^0.3.0",
|
|
46
|
+
"@tremolo-ui/functions": "^0.3.0",
|
|
46
47
|
"clsx": "^2.1.1",
|
|
47
48
|
"zustand": "^5.0.3"
|
|
48
49
|
},
|
|
@@ -1,39 +1,32 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useMemo, useSyncExternalStore } from 'react'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
export const PERMISSION_DENIED = 'PERMISSION_DENIED'
|
|
5
|
-
/** @private */
|
|
6
|
-
export const NOT_SUPPORTED = 'NOT_SUPPORTED'
|
|
3
|
+
import { createMIDIAccess } from '@tremolo-ui/dom'
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export {
|
|
6
|
+
NOT_SUPPORTED,
|
|
7
|
+
PERMISSION_DENIED,
|
|
8
|
+
type MIDIAccessError,
|
|
9
|
+
} from '@tremolo-ui/dom'
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Hooks for requesting MIDI access in the browser. The first argument allows you to choose whether to request access on mount.
|
|
13
13
|
*/
|
|
14
14
|
export function useMIDIAccess(requestOnMount = true) {
|
|
15
|
-
const
|
|
16
|
-
const [error, setError] = useState<MIDIAccessError | null>(null)
|
|
15
|
+
const instance = useMemo(() => createMIDIAccess(), [])
|
|
17
16
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
setMidiAccess(access)
|
|
24
|
-
setError(null)
|
|
25
|
-
})
|
|
26
|
-
.catch(() => {
|
|
27
|
-
setError(PERMISSION_DENIED)
|
|
28
|
-
})
|
|
29
|
-
} else {
|
|
30
|
-
setError(NOT_SUPPORTED)
|
|
31
|
-
}
|
|
32
|
-
}, [])
|
|
17
|
+
const { midiAccess, error } = useSyncExternalStore(
|
|
18
|
+
instance.subscribe,
|
|
19
|
+
instance.getState,
|
|
20
|
+
instance.getServerState,
|
|
21
|
+
)
|
|
33
22
|
|
|
34
23
|
useEffect(() => {
|
|
35
|
-
if (requestOnMount) request()
|
|
36
|
-
}, [
|
|
24
|
+
if (requestOnMount) instance.request()
|
|
25
|
+
}, [instance, requestOnMount])
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
return () => instance.destroy()
|
|
29
|
+
}, [instance])
|
|
30
|
+
|
|
31
|
+
return { request: instance.request, midiAccess, error }
|
|
39
32
|
}
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const MIDI_EVENT_TO_NUMBER = {
|
|
6
|
-
NOTE_ON: 0x90,
|
|
7
|
-
NOTE_OFF: 0x80,
|
|
8
|
-
PITCH_BEND: 0xe0,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// const NUMBER_TO_MIDI_EVENT: Record<number, string> = {
|
|
12
|
-
// 0x90: 'NOTE_ON',
|
|
13
|
-
// 0x80: 'NOTE_OFF',
|
|
14
|
-
// 0xe0: 'PITCH_BEND',
|
|
15
|
-
// }
|
|
3
|
+
import { createMIDIInput } from '@tremolo-ui/dom'
|
|
16
4
|
|
|
17
5
|
/**
|
|
18
6
|
* Hooks for handling note on/off events. To be used with useMIDIAccess. Internally uses useMIDIMessage.
|
|
@@ -23,27 +11,12 @@ export function useMIDIInput(
|
|
|
23
11
|
onNoteOffEvent?: (note: number) => void,
|
|
24
12
|
onPitchBendEvent?: (msb: number, lsb: number) => void,
|
|
25
13
|
) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (
|
|
35
|
-
kind == MIDI_EVENT_TO_NUMBER.NOTE_OFF ||
|
|
36
|
-
(kind == MIDI_EVENT_TO_NUMBER.NOTE_ON && event.data[2] == 0)
|
|
37
|
-
) {
|
|
38
|
-
onNoteOffEvent?.(event.data[1])
|
|
39
|
-
} else if (kind == MIDI_EVENT_TO_NUMBER.NOTE_ON) {
|
|
40
|
-
onNoteOnEvent?.(event.data[1], event.data[2])
|
|
41
|
-
} else if (kind == MIDI_EVENT_TO_NUMBER.PITCH_BEND) {
|
|
42
|
-
onPitchBendEvent?.(event.data[1], event.data[2])
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
[onNoteOffEvent, onNoteOnEvent, onPitchBendEvent],
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
useMIDIMessage(midiAccess, handleMIDIMessage)
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const instance = createMIDIInput(midiAccess, {
|
|
16
|
+
onNoteOnEvent,
|
|
17
|
+
onNoteOffEvent,
|
|
18
|
+
onPitchBendEvent,
|
|
19
|
+
})
|
|
20
|
+
return () => instance.destroy()
|
|
21
|
+
}, [midiAccess, onNoteOnEvent, onNoteOffEvent, onPitchBendEvent])
|
|
49
22
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useEffect } from 'react'
|
|
2
2
|
|
|
3
|
+
import { createMIDIMessage } from '@tremolo-ui/dom'
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Hooks for when you want to process MIDI events in more detail than useMIDIInput.
|
|
5
7
|
*/
|
|
@@ -8,16 +10,7 @@ export function useMIDIMessage(
|
|
|
8
10
|
onMIDIMessage: (event: MIDIMessageEvent) => void,
|
|
9
11
|
) {
|
|
10
12
|
useEffect(() => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
for (const input of midiAccess.inputs.values()) {
|
|
14
|
-
input.addEventListener('midimessage', onMIDIMessage)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return () => {
|
|
18
|
-
for (const input of midiAccess.inputs.values()) {
|
|
19
|
-
input.removeEventListener('midimessage', onMIDIMessage)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
13
|
+
const instance = createMIDIMessage(midiAccess, onMIDIMessage)
|
|
14
|
+
return () => instance.destroy()
|
|
22
15
|
}, [midiAccess, onMIDIMessage])
|
|
23
16
|
}
|