aboutsummaryrefslogtreecommitdiffstats
path: root/app/app.js
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2021-12-20 15:22:57 +0000
committers-ol <s+removethis@s-ol.nu>2021-12-20 15:22:57 +0000
commit930c3a7c40e1b1e5fa834167d4fc79a067c322f4 (patch)
treef7e850b53c7690d521951ae4b2909f6f1ce52b7f /app/app.js
parentadd blink function (diff)
downloadisomorphic-kb-explorer-930c3a7c40e1b1e5fa834167d4fc79a067c322f4.tar.gz
isomorphic-kb-explorer-930c3a7c40e1b1e5fa834167d4fc79a067c322f4.zip
add chord display
Diffstat (limited to 'app/app.js')
-rw-r--r--app/app.js189
1 files changed, 114 insertions, 75 deletions
diff --git a/app/app.js b/app/app.js
index 5fa04b4..172a908 100644
--- a/app/app.js
+++ b/app/app.js
@@ -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>
);
}