mirror of
https://github.com/hex248/tsos.git
synced 2026-02-07 18:23:05 +00:00
keyboard is bound to piano
This commit is contained in:
107
src/Index.tsx
107
src/Index.tsx
@@ -22,6 +22,62 @@ import * as Tone from "tone";
|
|||||||
import Layout from "./Layout";
|
import Layout from "./Layout";
|
||||||
import { cn } from "./lib/utils";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
|
const KEY_NOTE_BINDINGS = [
|
||||||
|
{ key: "z", note: "C", octaveOffset: -1 },
|
||||||
|
{ key: "x", note: "C#", octaveOffset: -1 },
|
||||||
|
{ key: "c", note: "D", octaveOffset: -1 },
|
||||||
|
{ key: "v", note: "D#", octaveOffset: -1 },
|
||||||
|
{ key: "b", note: "E", octaveOffset: -1 },
|
||||||
|
{ key: "n", note: "F", octaveOffset: -1 },
|
||||||
|
{ key: "m", note: "F#", octaveOffset: -1 },
|
||||||
|
{ key: ",", note: "G", octaveOffset: -1 },
|
||||||
|
{ key: ".", note: "G#", octaveOffset: -1 },
|
||||||
|
{ key: "a", note: "A", octaveOffset: -1 },
|
||||||
|
{ key: "s", note: "A#", octaveOffset: -1 },
|
||||||
|
{ key: "d", note: "B", octaveOffset: -1 },
|
||||||
|
{ key: "f", note: "C", octaveOffset: 0 },
|
||||||
|
{ key: "g", note: "C#", octaveOffset: 0 },
|
||||||
|
{ key: "h", note: "D", octaveOffset: 0 },
|
||||||
|
{ key: "j", note: "D#", octaveOffset: 0 },
|
||||||
|
{ key: "k", note: "E", octaveOffset: 0 },
|
||||||
|
{ key: "l", note: "F", octaveOffset: 0 },
|
||||||
|
{ key: ";", note: "F#", octaveOffset: 0 },
|
||||||
|
{ key: "'", note: "G", octaveOffset: 0 },
|
||||||
|
{ key: "q", note: "G#", octaveOffset: 0 },
|
||||||
|
{ key: "w", note: "A", octaveOffset: 0 },
|
||||||
|
{ key: "e", note: "A#", octaveOffset: 0 },
|
||||||
|
{ key: "r", note: "B", octaveOffset: 0 },
|
||||||
|
{ key: "t", note: "C", octaveOffset: 1 },
|
||||||
|
{ key: "y", note: "C#", octaveOffset: 1 },
|
||||||
|
{ key: "u", note: "D", octaveOffset: 1 },
|
||||||
|
{ key: "i", note: "D#", octaveOffset: 1 },
|
||||||
|
{ key: "o", note: "E", octaveOffset: 1 },
|
||||||
|
{ key: "p", note: "F", octaveOffset: 1 },
|
||||||
|
{ key: "[", note: "F#", octaveOffset: 1 },
|
||||||
|
{ key: "]", note: "G", octaveOffset: 1 },
|
||||||
|
{ key: "1", note: "G#", octaveOffset: 1 },
|
||||||
|
{ key: "2", note: "A", octaveOffset: 1 },
|
||||||
|
{ key: "3", note: "A#", octaveOffset: 1 },
|
||||||
|
{ key: "4", note: "B", octaveOffset: 1 },
|
||||||
|
{ key: "5", note: "C", octaveOffset: 2 },
|
||||||
|
{ key: "6", note: "C#", octaveOffset: 2 },
|
||||||
|
{ key: "7", note: "D", octaveOffset: 2 },
|
||||||
|
{ key: "8", note: "D#", octaveOffset: 2 },
|
||||||
|
{ key: "9", note: "E", octaveOffset: 2 },
|
||||||
|
{ key: "0", note: "F", octaveOffset: 2 },
|
||||||
|
{ key: "-", note: "F#", octaveOffset: 2 },
|
||||||
|
{ key: "=", note: "G", octaveOffset: 2 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const KEY_NOTE_MAP = new Map(KEY_NOTE_BINDINGS.map((binding) => [binding.key, binding]));
|
||||||
|
const COLOR_BY_NOTE = new Map(colorScale.map((entry) => [entry.note, entry.color]));
|
||||||
|
const MIN_OCTAVE = 1;
|
||||||
|
const MAX_OCTAVE = 8;
|
||||||
|
|
||||||
|
function clampOctave(value: number) {
|
||||||
|
return Math.min(MAX_OCTAVE, Math.max(MIN_OCTAVE, value));
|
||||||
|
}
|
||||||
|
|
||||||
function Index() {
|
function Index() {
|
||||||
const [dimensions, setDimensions] = useState({
|
const [dimensions, setDimensions] = useState({
|
||||||
width: window.innerWidth - 320,
|
width: window.innerWidth - 320,
|
||||||
@@ -82,6 +138,57 @@ function Index() {
|
|||||||
pitchTime,
|
pitchTime,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.repeat || event.metaKey || event.ctrlKey || event.altKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = event.target;
|
||||||
|
if (target instanceof HTMLElement) {
|
||||||
|
const tagName = target.tagName.toLowerCase();
|
||||||
|
if (
|
||||||
|
target.isContentEditable ||
|
||||||
|
tagName === "input" ||
|
||||||
|
tagName === "textarea" ||
|
||||||
|
tagName === "select"
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedKey = event.key.length === 1 ? event.key.toLowerCase() : event.key;
|
||||||
|
const binding = KEY_NOTE_MAP.get(normalizedKey);
|
||||||
|
if (!binding) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState((prev) => {
|
||||||
|
const targetOctave = clampOctave(prev.octave + binding.octaveOffset);
|
||||||
|
console.log(`${binding.note + targetOctave} ${prev.octave} + ${binding.octaveOffset}`);
|
||||||
|
const color = COLOR_BY_NOTE.get(binding.note) ?? prev.color;
|
||||||
|
|
||||||
|
void playPreviewSample({
|
||||||
|
preset: prev.preset,
|
||||||
|
roundness: prev.roundness,
|
||||||
|
size: prev.size,
|
||||||
|
grain: prev.grain,
|
||||||
|
note: binding.note,
|
||||||
|
octave: targetOctave,
|
||||||
|
synthNodes: synthRef.current,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prev,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [setState, synthRef]);
|
||||||
|
|
||||||
const sidebarContent = (
|
const sidebarContent = (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user