53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
import { Chord, Note } from 'tonal';
|
|
import { GUITAR_VOICINGS } from './guitar-voicings';
|
|
|
|
export interface GuitarVoicing {
|
|
/** Absolute fret numbers per string (low→high); null = muted, 0 = open */
|
|
frets: (number | null)[];
|
|
/** Lowest fret displayed on the diagram (0 = show nut) */
|
|
baseFret: number;
|
|
/** Absolute fret to draw a barre bar across, or null */
|
|
barre: number | null;
|
|
}
|
|
|
|
const ROOT_STRING_CHROMA: Record<'E' | 'A', number> = {
|
|
E: Note.chroma('E')!, // 4
|
|
A: Note.chroma('A')!, // 9
|
|
};
|
|
|
|
/**
|
|
* Returns the note names (e.g. ["C","E","G"]) for a chord string.
|
|
* Returns [] if the chord cannot be parsed.
|
|
*/
|
|
export function getPianoNotes(chord: string): string[] {
|
|
if (!chord) return [];
|
|
const parsed = Chord.get(chord);
|
|
if (!parsed.tonic || parsed.empty) return [];
|
|
return parsed.notes;
|
|
}
|
|
|
|
/**
|
|
* Returns a transposed GuitarVoicing for a chord string, or null if the
|
|
* chord quality has no template or the chord cannot be parsed.
|
|
*/
|
|
export function getGuitarVoicing(chord: string): GuitarVoicing | null {
|
|
if (!chord) return null;
|
|
const parsed = Chord.get(chord);
|
|
if (!parsed.tonic || parsed.empty) return null;
|
|
|
|
const template = GUITAR_VOICINGS[parsed.type];
|
|
if (!template) return null;
|
|
|
|
const rootChroma = ROOT_STRING_CHROMA[template.rootString];
|
|
const tonicChroma = Note.chroma(parsed.tonic);
|
|
if (tonicChroma === undefined) return null;
|
|
|
|
const shift = (tonicChroma - rootChroma + 12) % 12;
|
|
|
|
return {
|
|
frets: template.frets.map((f) => (f === null ? null : f + shift)),
|
|
baseFret: shift,
|
|
barre: template.barre === null ? null : template.barre + shift,
|
|
};
|
|
}
|