more things?
s-ol
2 years ago
1 | 1 | import css from './css'; |
2 | 2 | |
3 | 3 | const statbyte = (stat, chan) => (stat << 4) | chan; |
4 | const noteon = (key, vel=127, chan=0) => [statbyte(0b1001, chan), key, vel]; | |
5 | const noteoff = (key, vel=127, chan=0) => [statbyte(0b1000, chan), key, vel]; | |
6 | const ccchange = (ctl, val, chan=0) => [statbyte(0b1011, chan), ctl, val]; | |
4 | const NOTE_ON = 0b1001; | |
5 | const NOTE_OFF = 0b1000; | |
6 | const CCHANGE = 0b1011; | |
7 | ||
8 | /* | |
9 | const noteon = (key, vel=127, chan=0) => [statbyte(0b1001, chan), key, vel]; | |
10 | const noteoff = (key, vel=127, chan=0) => [statbyte(0b1000, chan), key, vel]; | |
11 | const ccchange = (ctl, val, chan=0) => [statbyte(0b1011, chan), ctl, val]; | |
12 | */ | |
7 | 13 | |
8 | 14 | const clamp = (val, min=0, max=1) => Math.max(min, Math.min(val, max)); |
9 | 15 | const ramp = i => new Array(i).fill(true).map((_, i) => i); |
10 | 16 | const mix = (i, a, b) => i * a + (1-i) * b; |
11 | ||
12 | window.ccchange = ccchange; | |
13 | window.noteon = noteon; | |
14 | 17 | |
15 | 18 | const parse = message => ({ |
16 | 19 | cmd: message.data[0] >> 4, |
35 | 38 | font-family: sans-serif; |
36 | 39 | } |
37 | 40 | |
38 | .app { | |
41 | .app, .keyboard { | |
39 | 42 | position: relative; |
40 | 43 | } |
41 | 44 | |
70 | 73 | } |
71 | 74 | `); |
72 | 75 | |
73 | const Hexagon = ({ x, y, children, state }) => { | |
76 | const Hexagon = ({ x, y, children, state, note, send }) => { | |
74 | 77 | if (rowStaggered) { |
75 | 78 | if (y % 2 == 0) |
76 | 79 | x += 0.5; |
89 | 92 | > |
90 | 93 | <div |
91 | 94 | className="inner" |
92 | onClick={() => console.info(x,y)} | |
95 | onMouseDown={() => send(NOTE_ON, note)} | |
96 | onMouseUp ={() => send(NOTE_OFF, note)} | |
93 | 97 | > |
94 | 98 | {children} |
95 | 99 | </div> |
99 | 103 | |
100 | 104 | |
101 | 105 | const range = i => new Array(i).fill(true); |
106 | class Keyboard extends React.Component { | |
107 | state = {}; | |
108 | ||
109 | onmessage = (message) => { | |
110 | switch (message.cmd) { | |
111 | case NOTE_ON: | |
112 | this.setState(old => ({ ...old, [message.note]: true })); | |
113 | break; | |
114 | ||
115 | case NOTE_OFF: | |
116 | this.setState(old => ({ ...old, [message.note]: false })); | |
117 | break; | |
118 | } | |
119 | } | |
120 | ||
121 | render() { | |
122 | const { w, h, send } = this.props; | |
123 | ||
124 | return ( | |
125 | <div className="keyboard"> | |
126 | {range(w).map((_, x) => ( | |
127 | range(h).map((_, y) => { | |
128 | let note = 36 + 2 * x + 6 * (h - y); | |
129 | if (y % 2 == 0) note += 1; | |
130 | return ( | |
131 | <Hexagon | |
132 | key={x + ',' + y} | |
133 | x={x} y={y} | |
134 | note={note} | |
135 | state={this.state[note]} | |
136 | send={send} | |
137 | > | |
138 | {note} | |
139 | </Hexagon> | |
140 | ); | |
141 | }) | |
142 | ))} | |
143 | </div> | |
144 | ); | |
145 | } | |
146 | } | |
102 | 147 | |
103 | 148 | export default class App extends React.Component { |
104 | state = {}; | |
149 | keyboardRef = React.createRef(); | |
105 | 150 | |
106 | 151 | componentDidUpdate(prevProps) { |
107 | 152 | if (prevProps.midiin) |
114 | 159 | onmessage = (e) => { |
115 | 160 | const message = parse(e); |
116 | 161 | |
162 | if (this.keyboardRef.current) | |
163 | this.keyboardRef.current.onmessage(message); | |
164 | ||
117 | 165 | switch (message.cmd) { |
118 | case 9: | |
119 | // NOTE_ON | |
120 | this.setState(old => ({ ...old, [message.note]: true })); | |
121 | break; | |
122 | ||
123 | case 8: | |
124 | // NOTE_OFF | |
125 | this.setState(old => ({ ...old, [message.note]: false })); | |
126 | break; | |
127 | ||
128 | case 11: | |
129 | // CC | |
166 | case NOTE_ON: | |
167 | console.info(message.note); | |
130 | 168 | break; |
131 | 169 | } |
132 | 170 | } |
133 | 171 | |
172 | send = (command, note) => { | |
173 | if (!this.props.midiout) | |
174 | return; | |
175 | ||
176 | const msg = [statbyte(command, 0), note, 127]; | |
177 | this.props.midiout.send(msg); | |
178 | } | |
179 | ||
134 | 180 | render() { |
135 | const xs = 10; | |
136 | const ys = 4; | |
137 | ||
138 | 181 | return ( |
139 | 182 | <div className="app"> |
140 | {range(xs).map((_, x) => ( | |
141 | range(ys).map((_, y) => { | |
142 | let note = 36 + 2 * x + 6 * (ys - y); | |
143 | if (y % 2 == 0) note += 1; | |
144 | return ( | |
145 | <Hexagon key={x + ',' + y} x={x} y={y} note={note} state={this.state[note]}> | |
146 | {note} | |
147 | </Hexagon> | |
148 | ); | |
149 | }) | |
150 | ))} | |
183 | <Keyboard ref={this.keyboardRef} w={10} h={4} send={this.send} /> | |
151 | 184 | </div> |
152 | 185 | ); |
153 | 186 | } |