1
1
import React , { useEffect , useLayoutEffect , useRef , useState } from 'react'
2
2
import useLocalStorageState from '../components/hooks/useLocalStorageState'
3
- import { WebMidi , Event } from 'webmidi'
3
+ import { WebMidi , Event , ControlChangeMessageEvent , NoteMessageEvent , MessageEvent } from 'webmidi'
4
4
import MidiDeviceSelector from '../components/MidiDeviceSelector'
5
- import {
6
- Button ,
7
- Checkbox ,
8
- ControlGroup ,
9
- FormGroup ,
10
- NumericInput ,
11
- } from '@blueprintjs/core'
5
+ import { Button , Checkbox , ControlGroup , FormGroup , NumericInput } from '@blueprintjs/core'
12
6
13
7
const eventTypes : { [ key : string ] : ( e : any ) => string } = {
14
8
activesensing : ( e : any ) => '' ,
15
9
clock : ( e : any ) => '' ,
16
- controlchange : ( e : { controller : { number : any ; name : any } ; value : any } ) =>
17
- [ e . controller . number , e . controller . name , e . value ] . join ( ' ' ) ,
18
- noteon : ( e : { note : { name : any ; octave : any } ; rawVelocity : any } ) =>
19
- [ e . note . name + e . note . octave , e . rawVelocity ] . join ( ' ' ) ,
20
- noteoff : ( e : { note : { name : any ; octave : any } ; rawVelocity : any } ) =>
21
- [ e . note . name + e . note . octave , e . rawVelocity ] . join ( ' ' ) ,
10
+ controlchange : ( e : ControlChangeMessageEvent ) => [ e . controller . number , e . controller . name , e . value ] . join ( ' ' ) ,
11
+ noteon : ( e : NoteMessageEvent ) => [ e . note . name + e . note . octave , e . rawValue ] . join ( ' ' ) ,
12
+ noteoff : ( e : NoteMessageEvent ) => [ e . note . name + e . note . octave , e . rawValue ] . join ( ' ' ) ,
13
+ sysex : ( e : MessageEvent ) =>
14
+ Array . from ( e . message . rawData . values ( ) )
15
+ . map ( ( x ) => x . toString ( 16 ) . padStart ( 2 , '0' ) . toUpperCase ( ) )
16
+ . join ( ' ' ) ,
22
17
}
23
18
24
19
export default function MidiMonitor ( ) {
25
20
const [ logs , setLogs ] = useState ( '' )
26
21
const [ tempo , setTempo ] = useLocalStorageState ( 'midi:monitor:tempo' , 100 )
27
- const [ deviceId , setDeviceId ] = useLocalStorageState (
28
- 'midi:monitor:device' ,
29
- ''
30
- )
31
- const [ selectedEventTypes , setEventTypes ] = useLocalStorageState (
32
- 'midi:monitor:eventTypes' ,
33
- [ 'noteon' ]
34
- )
35
- const [ selectedChannels , setChannels ] = useLocalStorageState (
36
- 'midi:monitor:channels' ,
37
- [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ]
38
- )
22
+ const [ deviceId , setDeviceId ] = useLocalStorageState ( 'midi:monitor:device' , '' )
23
+ const [ selectedEventTypes , setEventTypes ] = useLocalStorageState ( 'midi:monitor:eventTypes' , [ 'noteon' ] )
24
+ const [ selectedChannels , setChannels ] = useLocalStorageState ( 'midi:monitor:channels' , [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ] )
39
25
40
26
const device = WebMidi . getInputById ( deviceId )
41
27
@@ -46,11 +32,7 @@ export default function MidiMonitor() {
46
32
}
47
33
48
34
useEffect ( ( ) => {
49
- const listener = ( e : {
50
- type : keyof Event
51
- timestamp : number
52
- channel : { toString : ( ) => string }
53
- } ) => {
35
+ const listener = ( e : { type : keyof Event ; timestamp : number ; channel : { toString : ( ) => string } } ) => {
54
36
const getMessage = eventTypes [ e . type as any ] as any
55
37
if ( ! getMessage ) {
56
38
console . warn ( `No getMessage function for ${ e . type } ` )
@@ -73,15 +55,16 @@ export default function MidiMonitor() {
73
55
. join ( '\n' )
74
56
)
75
57
}
58
+ const options = {
59
+ channels : selectedChannels ,
60
+ }
76
61
if ( device ) {
77
62
selectedEventTypes . forEach ( ( eventType : any ) => {
78
- device . addListener ( eventType , listener , { channels : selectedChannels } )
63
+ device . addListener ( eventType , listener , options )
79
64
} )
80
65
return ( ) => {
81
66
selectedEventTypes . forEach ( ( eventType : any ) => {
82
- device . removeListener ( eventType , listener , {
83
- channels : selectedChannels ,
84
- } )
67
+ device . removeListener ( eventType , listener , options )
85
68
} )
86
69
}
87
70
}
@@ -97,22 +80,15 @@ export default function MidiMonitor() {
97
80
< div >
98
81
< FormGroup >
99
82
< ControlGroup >
100
- < MidiDeviceSelector
101
- mode = "input"
102
- label = "Input"
103
- value = { deviceId }
104
- onChange = { ( v : any ) => setDeviceId ( v ) }
105
- />
83
+ < MidiDeviceSelector mode = "input" label = "Input" value = { deviceId } onChange = { ( v : any ) => setDeviceId ( v ) } />
106
84
< NumericInput
107
85
leftIcon = "time"
108
86
// rightElement={<Tag minimal>bpm</Tag>}
109
87
placeholder = "Tempo"
110
88
value = { tempo }
111
89
min = { 32 }
112
90
max = { 240 }
113
- onChange = { ( e : { target : { value : any } } ) =>
114
- setTempo ( e . target . value )
115
- }
91
+ onChange = { ( e : { target : { value : any } } ) => setTempo ( e . target . value ) }
116
92
/>
117
93
< Button onClick = { clear } > Clear</ Button >
118
94
</ ControlGroup >
@@ -127,9 +103,7 @@ export default function MidiMonitor() {
127
103
if ( ( e . target as HTMLInputElement ) . checked ) {
128
104
setEventTypes ( ( t : any ) => [ ...t , eventType ] )
129
105
} else {
130
- setEventTypes ( ( t : any [ ] ) =>
131
- t . filter ( ( e : string ) => e !== eventType )
132
- )
106
+ setEventTypes ( ( t : any [ ] ) => t . filter ( ( e : string ) => e !== eventType ) )
133
107
}
134
108
} }
135
109
>
@@ -138,31 +112,24 @@ export default function MidiMonitor() {
138
112
) ) }
139
113
</ FormGroup >
140
114
< FormGroup label = "Channels" >
141
- { [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ] . map (
142
- ( channel ) => (
143
- < Checkbox
144
- key = { channel }
145
- inline = { true }
146
- checked = { selectedChannels . includes ( channel ) }
147
- onChange = { ( e ) => {
148
- if ( ( e . target as HTMLInputElement ) . checked ) {
149
- setChannels ( ( ch : any ) => [ ...ch , channel ] )
150
- } else {
151
- setChannels ( ( ch : any [ ] ) =>
152
- ch . filter ( ( e : number ) => e !== channel )
153
- )
154
- }
155
- } }
156
- >
157
- { channel }
158
- </ Checkbox >
159
- )
160
- ) }
115
+ { [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 ] . map ( ( channel ) => (
116
+ < Checkbox
117
+ key = { channel }
118
+ inline = { true }
119
+ checked = { selectedChannels . includes ( channel ) }
120
+ onChange = { ( e ) => {
121
+ if ( ( e . target as HTMLInputElement ) . checked ) {
122
+ setChannels ( ( ch : any ) => [ ...ch , channel ] )
123
+ } else {
124
+ setChannels ( ( ch : any [ ] ) => ch . filter ( ( e : number ) => e !== channel ) )
125
+ }
126
+ } }
127
+ >
128
+ { channel }
129
+ </ Checkbox >
130
+ ) ) }
161
131
</ FormGroup >
162
- < pre
163
- ref = { logRef }
164
- className = "bg-black bg-opacity-20 overflow-y-scroll h-[500px]"
165
- >
132
+ < pre ref = { logRef } className = "bg-black bg-opacity-20 overflow-y-scroll h-[500px]" >
166
133
< code className = "py-4 block" > { logs } </ code >
167
134
</ pre >
168
135
</ div >
0 commit comments