35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
import type { Song } from "./types";
|
|
|
|
const NOTES_SHARP = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"];
|
|
const NOTES_FLAT = ["C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"];
|
|
|
|
function transposeChord(chord: string, semitones: number): string {
|
|
const match = chord.match(/^([A-G][#b]?)(.*)/);
|
|
if (!match) return chord;
|
|
const [, root, descriptor] = match;
|
|
const idx = NOTES_SHARP.indexOf(root) !== -1
|
|
? NOTES_SHARP.indexOf(root)
|
|
: NOTES_FLAT.indexOf(root);
|
|
if (idx === -1) return chord;
|
|
const newIdx = ((idx + semitones) % 12 + 12) % 12;
|
|
const notes = semitones >= 0 ? NOTES_SHARP : NOTES_FLAT;
|
|
return notes[newIdx] + descriptor;
|
|
}
|
|
|
|
export function transposeSong(song: Song, semitones: number): Song {
|
|
if (semitones === 0) return song;
|
|
return {
|
|
...song,
|
|
sections: song.sections.map((section) => ({
|
|
...section,
|
|
lines: section.lines.map((line) => ({
|
|
...line,
|
|
chords: line.chords.map((cp) => ({
|
|
...cp,
|
|
chord: transposeChord(cp.chord, semitones),
|
|
})),
|
|
})),
|
|
})),
|
|
};
|
|
}
|