Skip to content

Commit

Permalink
refactor: Simplify chord shape types and improve code readability
Browse files Browse the repository at this point in the history
  • Loading branch information
drikusroor committed Dec 26, 2024
1 parent 04a73a4 commit c44fe28
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 41 deletions.
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import { getChordShapes, chordDistance } from "./utils/chordLogic";
import { ChordShapeRefined } from "./types/chord-shape";
import { ChordDiagram } from "./ChordDiagram";
import {
chordToNotes,
findChordShapes,
findInterchangeableChords,
standardGuitar,
} from "./utils/chordLogicV2";

Expand Down
52 changes: 34 additions & 18 deletions src/ChordDiagram.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import React from 'react';
import { ChordShapeRefined } from './types/chord-shape';
import { audioService } from './utils/audioService';
import React from "react";
import { ChordShapeRefined } from "./types/chord-shape";
import { audioService } from "./utils/audioService";

const STRING_NAMES = ["e", "b", "g", "d", "a", "e"];

interface ChordDiagramProps {
frets: ChordShapeRefined['frets'];
fingers: ChordShapeRefined['fingers'];
onAdd?: () => void; // Make onAdd optional
frets: ChordShapeRefined["frets"];
fingers: ChordShapeRefined["fingers"];
onAdd?: () => void; // Make onAdd optional
}

export const ChordDiagram: React.FC<ChordDiagramProps> = ({ frets, fingers = [], onAdd }) => {
export const ChordDiagram: React.FC<ChordDiagramProps> = ({
frets,
fingers = [],
onAdd,
}) => {
const invertedFrets = [...frets].reverse();
const invertedFingers = [...fingers].reverse();

const hardToPlay = fingers.reduce((acc, f) => acc + f, 0) <= 0;
const hardToPlay =
fingers
.filter((f) => typeof f === "number")
.reduce((acc, f) => acc + f, 0) <= 0;

const handlePlay = () => {
audioService.playChord(frets);
Expand All @@ -37,27 +44,36 @@ export const ChordDiagram: React.FC<ChordDiagramProps> = ({ frets, fingers = [],
className="hidden group-hover:inline-block p-1 rounded-full w-8 h-8 bg-green-500 hover:bg-green-600 text-white transition-colors"
title="Play chord (arpeggiated)"
>
{/* utf play icon */}
{/* utf play icon */}
</button>
</div>

<div className="space-y-2">
{STRING_NAMES.map((stringName, i) => {
const f = invertedFrets[i];
const fi = invertedFingers[i];
let fingerName = '';
let fingerName = "";
switch (fi) {
case 1: fingerName = '(Index)'; break;
case 2: fingerName = '(Middle)'; break;
case 3: fingerName = '(Ring)'; break;
case 4: fingerName = '(Pinky)'; break;
case 1:
fingerName = "(Index)";
break;
case 2:
fingerName = "(Middle)";
break;
case 3:
fingerName = "(Ring)";
break;
case 4:
fingerName = "(Pinky)";
break;
}

return (
<div key={i} className="flex items-center text-gray-700">
<span className="w-6 font-bold text-indigo-600">{stringName}</span>
<span className="mx-2 bg-gray-400 w-4 h-0.5"/>
<span className="w-6 font-bold text-indigo-600">
{stringName}
</span>
<span className="mx-2 bg-gray-400 w-4 h-0.5" />
<span className="w-4 text-center tabular-nums">{f}</span>
{fingerName && (
<span className="ml-2" title={`Finger ${fi}`}>
Expand All @@ -78,4 +94,4 @@ export const ChordDiagram: React.FC<ChordDiagramProps> = ({ frets, fingers = [],
);
};

export default ChordDiagram;
export default ChordDiagram;
13 changes: 8 additions & 5 deletions src/types/chord-shape.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
export type Fret = number | string;
export type Finger = number | string;

type ChordShape = {
frets: (string | number)[];
fingers?: (number | 0)[];
fingersOpen?: (number | 0)[];
frets: Fret[];
fingers?: Finger[];
fingersOpen?: Finger[];
};

export type ChordShapeRefined = {
frets: (string | number)[];
fingers: (number | 0)[];
frets: Fret[];
fingers: Finger[];
}

export default ChordShape;
4 changes: 3 additions & 1 deletion src/utils/audioService.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Fret } from "../types/chord-shape";

// audioService.ts
const NOTE_FREQUENCIES: Record<string, number> = {
'E2': 82.41,
Expand Down Expand Up @@ -75,7 +77,7 @@ const NOTE_FREQUENCIES: Record<string, number> = {
return baseFreq * Math.pow(2, fret / 12);
}

playChord(frets: (string | number)[]) {
playChord(frets: Fret[]) {
if (this.isPlaying) {
// stop previous chord with short fade out
this.isPlaying = false;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/chordLogic.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import G_CHORDS from "../data/chords";
import ChordShape, { ChordShapeRefined } from "../types/chord-shape";
import ChordShape, { ChordShapeRefined, Fret } from "../types/chord-shape";

export const NOTE_MAP: Record<string, number> = {
C: 0,
Expand Down Expand Up @@ -107,7 +107,7 @@ function transposeChordShape(gShape: ChordShape, fromRoot: string, toRoot: strin
const fromSemitone = NOTE_MAP[fromRoot];
const toSemitone = NOTE_MAP[toRoot];
const interval = (toSemitone - fromSemitone + 12) % 12;
let newFrets: (string | number)[] = [];
let newFrets: Fret[] = [];

for (let i = 0; i < gShape.frets.length; i++) {
const f = gShape.frets[i];
Expand Down
27 changes: 14 additions & 13 deletions src/utils/chordLogicV2.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// chordLogicV2.ts

import { ChordShapeRefined, Finger, Fret } from "../types/chord-shape";

export type Chord = {
root: string;
chordType: string;
Expand Down Expand Up @@ -49,7 +51,7 @@ export const standardGuitar: Instrument = {
};

export type ChordShape = {
frets: (number | "x")[]; // 'x' represents a muted string
frets: Fret[]; // 'x' represents a muted string
fingers?: (number | 0)[]; // Optional finger positions (not implemented)
fingersOpen?: (number | 0)[]; // Optional open string indicators (not implemented)
};
Expand Down Expand Up @@ -437,7 +439,7 @@ export function findChordShapes(
chordName: string,
instrument: Instrument,
limit: number = 5
): ChordShape[] {
): ChordShapeRefined[] {
const chordNotesObj = chordToNotes(chordName, true); // Normalize notes for comparison
if (!chordNotesObj) {
console.warn(`Cannot find notes for chord: ${chordName}`);
Expand All @@ -448,10 +450,10 @@ export function findChordShapes(
const bassNote = chordNotesObj.bass; // Number (0-11) or undefined

// For each string, list possible frets that can produce required notes or 'x' to mute
const stringFretOptions: (number | "x")[][] = instrument.tuning.map(
const stringFretOptions: Fret[][] = instrument.tuning.map(
(string) => {
const openNote = string.note % 12;
const options: (number | "x")[] = [];
const options: Fret[] = [];

// List frets 0-12 that produce required notes
for (let fret = 0; fret <= 12; fret++) {
Expand All @@ -468,7 +470,7 @@ export function findChordShapes(
}
);

const results: ChordShape[] = [];
const results: ChordShapeRefined[] = [];

/**
* Recursive backtracking function to find chord shapes.
Expand All @@ -480,7 +482,7 @@ export function findChordShapes(
* @param bassAssigned Whether the bass note has been assigned.
*/
function backtrack(
currentFretList: (number | "x")[],
currentFretList: Fret[],
currentNotesCovered: Set<number>,
stringIndex: number,
minFret: number | null,
Expand Down Expand Up @@ -523,7 +525,6 @@ export function findChordShapes(
results.push({
frets: [...currentFretList],
fingers,
name: chordName,
});
}
return;
Expand Down Expand Up @@ -559,9 +560,9 @@ export function findChordShapes(
// Update min and max frets
if (option !== 0 && option !== "x") {
newMinFret =
newMinFret === null ? option : Math.min(newMinFret, option);
newMinFret === null ? option as number : Math.min(newMinFret, option as number);
newMaxFret =
newMaxFret === null ? option : Math.max(newMaxFret, option);
newMaxFret === null ? option as number : Math.max(newMaxFret, option as number);
}

// Check fret span
Expand Down Expand Up @@ -632,7 +633,7 @@ export function findChordShapes(
return results;
}

export function getChordFingers(frets: (number | "x")[]): (number)[] {
export function getChordFingers(frets: Fret[]): (number)[] {
// 1 = Index, 2 = Middle, 3 = Ring, 4 = Pinky

const fingers = new Array(frets.length).fill("x");
Expand Down Expand Up @@ -693,7 +694,7 @@ export function getChordFingers(frets: (number | "x")[]): (number)[] {
*/
function assignFinger(
fingerValue: number,
frets: (number | "x")[],
frets: Fret[],
fingers: (number | string)[]
) {
// Gather unassigned notes (ignore "x", 0, or already assigned)
Expand Down Expand Up @@ -726,8 +727,8 @@ function assignFinger(
* to pass chords like 133111 (Fm) => 134111 or 133211 (F) => 134211, etc.
*/
function postProcessBarreOneThree(
frets: (number | "x")[],
fingers: (number | string)[]
frets: Fret[],
fingers: Finger[]
) {
// 1) Check if we have a "barre at 1"
const minFret = Math.min(...frets.filter((f) => typeof f === "number" && f > 0) as number[]);
Expand Down

0 comments on commit c44fe28

Please sign in to comment.