From 32b471aa58182c37d5e41b3327f13188f51128dd Mon Sep 17 00:00:00 2001 From: Amit S Namboothiry Date: Sun, 2 Jun 2024 16:44:54 +0530 Subject: [PATCH] Improve Ukulele fingerings --- src/fingerings.js | 23 ++++++++++++++++------- src/fingerings.test.js | 20 ++++++++++++++++++-- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/fingerings.js b/src/fingerings.js index be1c0a1..3caf496 100644 --- a/src/fingerings.js +++ b/src/fingerings.js @@ -137,15 +137,19 @@ function findFingerings(notes, optionalNotes = [], bass = notes[0], tuning = 'E- let fingerings = []; let positions = findPositions(notes, tuning); + let isTuningAscending = JSON.stringify(tuning) === JSON.stringify(originalTuning); const requiredNotes = notes.slice().filter(n => !optionalNotes.includes(n)); let maxBassStringIndex = tuning.length - requiredNotes.length; if (!requiredNotes.includes(bass)) { maxBassStringIndex--; } - let bassPositions = positions - .filter(p => areNotesEqual(p.note, bass)) - .filter(p => p.stringIndex <= maxBassStringIndex); + let bassPositions = positions; + if (isTuningAscending) { + bassPositions = positions + .filter(p => areNotesEqual(p.note, bass)) + .filter(p => p.stringIndex <= maxBassStringIndex); + } for (let i = 0; i < bassPositions.length; i++) { let bassPosition = bassPositions[i]; @@ -162,8 +166,11 @@ function findFingerings(notes, optionalNotes = [], bass = notes[0], tuning = 'E- return position.fret === 0 || nonZeroFrets.every(fret => Math.abs(position.fret - fret) <= MAX_FRET_DISTANCE); }); - const sensiblePositions = reachablePositions.filter(p => - tonal.distance(bassPosition.note, p.note)[0] !== '-'); + let sensiblePositions = reachablePositions; + if (isTuningAscending) { + sensiblePositions = reachablePositions.filter(p => + tonal.distance(bassPosition.note, p.note)[0] !== '-'); + } if (sensiblePositions.length) { return sensiblePositions.map(position => [...fingering, position]); } @@ -233,12 +240,14 @@ function findFingerings(notes, optionalNotes = [], bass = notes[0], tuning = 'E- return 0 || notLessThanFourStrings || lessMutedStringsInBetween + || (isTuningAscending ? 0 : lowerMaxFret) + || (isTuningAscending ? 0 : easier) || lessRepeatingNotes || easierThan14 || lowerBassFret || easierThan12 - || lowerMaxFret - || easier + || (isTuningAscending ? lowerMaxFret : 0) + || (isTuningAscending ? easier : 0) || 0; }); return fingerings; diff --git a/src/fingerings.test.js b/src/fingerings.test.js index 86e9b61..550589b 100644 --- a/src/fingerings.test.js +++ b/src/fingerings.test.js @@ -104,68 +104,84 @@ describe('fingerings', () => { expect(matching).toBeTruthy(); }); - const pos = (s, i) => { + const pos = (s, i, tuning = 'E-A-D-G-B-E') => { const c = findChord(s); const { notes, optionalNotes } = c; - return findFingerings(notes, optionalNotes)[i].positionString; + return findFingerings(notes, optionalNotes, notes[0], tuning)[i].positionString; }; it('sort C fingerings correctly', () => { expect(pos('C', 0)).toBe('x32010'); + expect(pos('C', 0, 'G4-C4-E4-A4')).toBe('0003'); }); it('sort Cm fingerings correctly', () => { expect(pos('Cm', 0)).toBe('x35543'); + expect(pos('Cm', 0, 'G4-C4-E4-A4')).toBe('0333'); }); it('sort C# fingerings correctly', () => { expect(pos('C#', 0)).toBe('x46664'); + expect(pos('C#', 0, 'G4-C4-E4-A4')).toBe('1114'); }); it('sort D fingerings correctly', () => { expect(pos('D', 0)).toBe('xx0232'); + expect(pos('D', 0, 'G4-C4-E4-A4')).toBe('2220'); }); it('sort Dm fingerings correctly', () => { expect(pos('Dm', 0)).toBe('xx0231'); + expect(pos('Dm', 0, 'G4-C4-E4-A4')).toBe('2210'); }); it('sort E fingerings correctly', () => { expect(pos('E', 0)).toBe('022100'); + expect(pos('E', 0, 'G4-C4-E4-A4')).toBe('4442'); }); it('sort Em fingerings correctly', () => { expect(pos('Em', 0)).toBe('022000'); + expect(pos('Em', 0, 'G4-C4-E4-A4')).toBe('0432'); }); it('sort F fingerings correctly', () => { expect(pos('F', 0)).toBe('133211'); expect(pos('F', 1)).toBe('xx3211'); + expect(pos('F', 0, 'G4-C4-E4-A4')).toBe('2010'); }); it('sort Fm fingerings correctly', () => { expect(pos('Fm', 0)).toBe('133111'); + expect(pos('Fm', 0, 'G4-C4-E4-A4')).toBe('1013'); }); it('sort G fingerings correctly', () => { expect(pos('G', 0)).toBe('320003'); expect(pos('G', 1)).toBe('320033'); + expect(pos('G', 0, 'G4-C4-E4-A4')).toBe('0232'); }); it('sort Gm fingerings correctly', () => { expect(pos('Gm', 0)).toBe('355333'); + expect(pos('Gm', 0, 'G4-C4-E4-A4')).toBe('0231'); }); it('sort A fingerings correctly', () => { expect(pos('A', 0)).toBe('x02220'); // expect(pos('A', 1)).toBe('577655'); + expect(pos('A', 0, 'G4-C4-E4-A4')).toBe('2100'); }); it('sort Am fingerings correctly', () => { expect(pos('Am', 0)).toBe('x02210'); + expect(pos('Am', 0, 'G4-C4-E4-A4')).toBe('2000'); }); it('sort B fingerings correctly', () => { expect(pos('B', 0)).toBe('x24442'); expect(pos('B', 1)).toBe('799877'); + expect(pos('B', 0, 'G4-C4-E4-A4')).toBe('4322'); }); it('sort B7 fingerings correctly', () => { expect(pos('B7', 0)).toBe('x21202'); + expect(pos('B7', 0, 'G4-C4-E4-A4')).toBe('2322'); }); it('sort Bm fingerings correctly', () => { expect(pos('Bm', 0)).toBe('x24432'); + expect(pos('Bm', 0, 'G4-C4-E4-A4')).toBe('4222'); }); }); \ No newline at end of file