diff options
| author | s-ol <s+removethis@s-ol.nu> | 2021-12-20 15:22:57 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2021-12-20 15:22:57 +0000 |
| commit | 930c3a7c40e1b1e5fa834167d4fc79a067c322f4 (patch) | |
| tree | f7e850b53c7690d521951ae4b2909f6f1ce52b7f /app/app.js | |
| parent | add blink function (diff) | |
| download | isomorphic-kb-explorer-930c3a7c40e1b1e5fa834167d4fc79a067c322f4.tar.gz isomorphic-kb-explorer-930c3a7c40e1b1e5fa834167d4fc79a067c322f4.zip | |
add chord display
Diffstat (limited to 'app/app.js')
| -rw-r--r-- | app/app.js | 189 |
1 files changed, 114 insertions, 75 deletions
@@ -1,6 +1,6 @@ import React from 'react'; -import { pos, height } from './style'; -import notes from './notes'; +import { pos, width, height } from './style'; +import * as notes from './notes'; const statbyte = (stat, chan) => (stat << 4) | chan; const NOTE_ON = 0b1001; @@ -78,61 +78,74 @@ const layouts = { const range = i => new Array(i).fill(true); -class Keyboard extends React.Component { - state = {}; +const Keyboard = ({ w, h, send, layout: { major, map }, showMidi, state }) => ( + <div + className={`keyboard ${major}-major`} + style={{ + height: height(h, major), + width: width(w, major), + }} + > + {range(w).map((_, x) => ( + range(h).map((_, y) => { + const note = map([x, y], [w, h]); + return ( + <Hexagon + key={x + ',' + y} + x={x} y={y} + note={note} + state={state[note]} + send={send} + major={major} + > + {showMidi ? note : notes.music[note % 12]} + </Hexagon> + ); + }) + ))} + </div> +); + +const ChordView = ({ chord }) => { + const min = chord[0]; - onmessage = (message) => { - switch (message.cmd) { - case NOTE_ON: - this.setState(old => ({ ...old, [message.note]: true })); - break; - - case NOTE_OFF: - this.setState(old => ({ ...old, [message.note]: false })); - break; - } - } - - render() { - const { w, h, send, layout, showMidi } = this.props; - const { major, map } = layouts[layout]; - - return ( - <div - className={`keyboard ${major}-major`} - style={{ - height: height(h, major), - }} - > - {range(w).map((_, x) => ( - range(h).map((_, y) => { - const note = map([x, y], [w, h]); - return ( - <Hexagon - key={x + ',' + y} - x={x} y={y} - note={note} - state={this.state[note]} - send={send} - major={major} - > - {showMidi ? notes[note % 12] : note} - </Hexagon> - ); - }) - ))} - </div> - ); - } -} + return ( + <div className="chord"> + {range(13).map((_,i) => ( + <div + key={i} + className="blip" + /> + ))} + {chord.map((n) => ( + <div + key={n} + className="note" + style={{ bottom: `${((n - min) % 12) / 12 * 100 - 3}%` }} + > + {(n - min) && (<span>+{n - min}</span>)} + </div> + ))} + </div> + ); +}; export default class App extends React.Component { - keyboardRef = React.createRef(); - state = { layout: 'wicki_hayden', + notes: {}, }; + componentDidMount() { + document.addEventListener("keydown", this.onkeydown); + document.addEventListener("keyup", this.onkeyup); + } + + componentWillUnmount() { + document.removeEventListener("keydown", this.onkeydown); + document.removeEventListener("keyup", this.onkeyup); + } + componentDidUpdate(prevProps) { if (prevProps.midiin) prevProps.midiin.onmidimessage = null; @@ -142,41 +155,62 @@ export default class App extends React.Component { } blink = (note) => { - if (this.keyboardRef.current) { - const { current } = this.keyboardRef; - current.onmessage({ cmd: NOTE_ON, note }); - setTimeout(() => current.onmessage({ cmd: NOTE_OFF, note }), 250); - } + this.onmessage({ cmd: NOTE_ON, note }); + setTimeout(() => this.onmessage({ cmd: NOTE_OFF, note }), 250); + } + + send = (command, note) => { + if (!this.props.midiout) + return; + + const msg = [statbyte(command, 0), note, 127]; + this.props.midiout.send(msg); + } + + onlayout = (e) => { + this.setState({ layout: e.target.value }); } onmessage = (e) => { const message = parse(e); - if (this.keyboardRef.current) - this.keyboardRef.current.onmessage(message); - switch (message.cmd) { case NOTE_ON: console.info(message.note); break; } - } - send = (command, note) => { - if (!this.props.midiout) - return; + switch (message.cmd) { + case NOTE_ON: + this.setState(({ notes }) => ({ notes: { ...notes, [message.note]: true } })); + break; - const msg = [statbyte(command, 0), note, 127]; - this.props.midiout.send(msg); + case NOTE_OFF: + this.setState(({ notes }) => ({ notes: { ...notes, [message.note]: false } })); + break; + } } - onlayout = (e) => { - this.setState({ layout: e.target.value }); + onkeydown = (e) => { + const note = notes.key2midi[e.code]; + if (!note) return; + e.preventDefault(); + this.setState(({ notes }) => ({ notes: { ...notes, [note]: true } })); + } + + onkeyup = (e) => { + const note = notes.key2midi[e.code]; + if (!note) return; + e.preventDefault(); + this.setState(({ notes }) => ({ notes: { ...notes, [note]: false } })); } render() { const { configure, showMidi } = this.props; - const { layout } = this.state; + const { layout, notes } = this.state; + + const chord = Object.entries(notes).filter(([note, on]) => on).map(([k, _]) => k); + chord.sort(); return ( <div className="app"> @@ -189,14 +223,19 @@ export default class App extends React.Component { </select> </label> - <Keyboard - ref={this.keyboardRef} - w={12} - h={4} - send={this.send} - layout={layout} - showMidi={showMidi} - /> + <div className="main"> + <Keyboard + w={12} + h={4} + send={this.send} + layout={layouts[layout]} + showMidi={showMidi} + state={notes} + /> + <ChordView + chord={chord} + /> + </div> </div> ); } |
