Skip to content

Commit 7659db1

Browse files
Make renderChord() use render key modifier (#1535)
1 parent 08af7f6 commit 7659db1

File tree

3 files changed

+104
-15
lines changed

3 files changed

+104
-15
lines changed

src/chord_sheet/song.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -252,18 +252,33 @@ class Song extends MetadataAccessors {
252252
* @param {boolean} [options.normalizeChordSuffix=false] whether to normalize the chord suffixes after transposing
253253
* @returns {Song} The transposed song
254254
*/
255-
transpose(delta: number, { normalizeChordSuffix = false } = {}): Song {
255+
transpose(
256+
delta: number,
257+
{ modifier, normalizeChordSuffix = false }:
258+
{ modifier?: Modifier | null, normalizeChordSuffix?: boolean } = {},
259+
): Song {
256260
let transposedKey: Key | null = null;
257261
const song = (this as Song);
258262

259263
return song.mapItems((item) => {
260264
if (item instanceof Tag && item.name === KEY) {
261265
transposedKey = Key.wrapOrFail(item.value).transpose(delta);
266+
267+
if (modifier) {
268+
transposedKey = transposedKey.useModifier(modifier);
269+
}
270+
262271
return item.set({ value: transposedKey.toString() });
263272
}
264273

265274
if (item instanceof ChordLyricsPair) {
266-
return item.transpose(delta, transposedKey, { normalizeChordSuffix });
275+
let chord = item.transpose(delta, transposedKey, { normalizeChordSuffix });
276+
277+
if (modifier) {
278+
chord = chord.useModifier(modifier);
279+
}
280+
281+
return chord;
267282
}
268283

269284
return item;
@@ -308,7 +323,7 @@ class Song extends MetadataAccessors {
308323
const currentKey = this.requireCurrentKey();
309324
const targetKey = Key.wrapOrFail(newKey);
310325
const delta = currentKey.distanceTo(targetKey);
311-
const transposedSong = this.transpose(delta);
326+
const transposedSong = this.transpose(delta, { modifier: targetKey.modifier });
312327

313328
if (targetKey.modifier) {
314329
return transposedSong.useModifier(targetKey.modifier);

src/helpers.ts

+55-9
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Song from './chord_sheet/song';
55
import { CAPO, CHORD_STYLE, ChordType } from './chord_sheet/tag';
66
import Line from './chord_sheet/line';
77
import FormattingContext from './formatter/formatting_context';
8+
import { Modifier } from './constants';
89

910
export function transposeDistance(transposeKey: string, songKey: string): number {
1011
if (/^\d+$/.test(transposeKey)) {
@@ -61,6 +62,42 @@ interface RenderChordOptions {
6162
decapo?: boolean;
6263
}
6364

65+
function effectiveModifier(renderKey: Key | null, contextKey: Key | null): Modifier | null {
66+
if (renderKey?.modifier) {
67+
return renderKey.modifier;
68+
}
69+
70+
if (contextKey?.modifier) {
71+
return contextKey.modifier;
72+
}
73+
74+
return null;
75+
}
76+
77+
function shapeChord(
78+
{
79+
chord,
80+
effectiveTransposeDistance,
81+
modifier,
82+
normalizeChords,
83+
effectiveKey,
84+
chordStyle,
85+
} : {
86+
chord: Chord,
87+
effectiveTransposeDistance: number,
88+
modifier: Modifier | null,
89+
normalizeChords: boolean,
90+
effectiveKey: Key | null,
91+
chordStyle: ChordType,
92+
},
93+
) {
94+
const transposedChord = chord.transpose(effectiveTransposeDistance);
95+
const correctedChord = modifier ? transposedChord.useModifier(modifier) : transposedChord;
96+
const normalizedChord = (normalizeChords ? correctedChord.normalize(effectiveKey) : transposedChord);
97+
98+
return changeChordType(normalizedChord, chordStyle, effectiveKey);
99+
}
100+
64101
/**
65102
* Renders a chord in the context of a line and song and taking into account some options
66103
* @param chordString The chord to render
@@ -85,21 +122,30 @@ export function renderChord(
85122
}: RenderChordOptions = {},
86123
): string {
87124
const chord = Chord.parse(chordString);
88-
const songKey = song.key;
89-
const capoString = song.metadata.getSingle(CAPO);
90-
const capo = (decapo && capoString) ? parseInt(capoString, 10) : null;
91-
const chordStyle = song.metadata.getSingle(CHORD_STYLE) as ChordType;
92125

93126
if (!chord) {
94127
return chordString;
95128
}
96129

97-
const effectiveTransposeDistance = chordTransposeDistance(capo, line.transposeKey, songKey, renderKey);
98-
const effectiveKey = renderKey || Key.wrap(line.key || song.key)?.transpose(effectiveTransposeDistance) || null;
99-
const transposedChord = chord.transpose(effectiveTransposeDistance);
100-
const normalizedChord = (normalizeChords ? transposedChord.normalize(effectiveKey) : transposedChord);
130+
const songKey = song.key;
131+
const capoString = song.metadata.getSingle(CAPO);
132+
const capo = (decapo && capoString) ? parseInt(capoString, 10) : null;
133+
const chordStyle = song.metadata.getSingle(CHORD_STYLE) as ChordType;
101134

102-
return changeChordType(normalizedChord, chordStyle, effectiveKey).toString({ useUnicodeModifier });
135+
const effectiveTransposeDistance = chordTransposeDistance(capo, line.transposeKey, songKey, renderKey);
136+
const contextKey = Key.wrap(line.key || song.key);
137+
const modifier = effectiveModifier(renderKey, contextKey);
138+
const effectiveKey = renderKey || contextKey?.transpose(effectiveTransposeDistance) || null;
139+
140+
return shapeChord({
141+
chord,
142+
effectiveTransposeDistance,
143+
modifier,
144+
normalizeChords,
145+
effectiveKey,
146+
chordStyle,
147+
})
148+
.toString({ useUnicodeModifier });
103149
}
104150

105151
/**

test/helpers/render_chord.test.ts

+31-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ describe('renderChord helper', () => {
3636
25 | "G" | 3 | | "A" | "F" | true | "C#m7" |
3737
26 | "G" | 3 | "Bb" | | "F" | true | "Bm7" |
3838
27 | "G" | 3 | "Bb" | "A" | | true | "Ebm7" |
39-
28 | "G" | 3 | "Bb" | "A" | "F" | true | "C#m7" |
39+
28 | "G" | 3 | "Bb" | "A" | "F" | true | "Dbm7" |
4040
29 | | 3 | | | | false | "Em7" |
4141
30 | | 3 | | | "F" | false | "Em7" |
4242
31 | | 3 | | "A" | | false | "Em7" |
@@ -45,7 +45,7 @@ describe('renderChord helper', () => {
4545
34 | | 3 | "Bb" | | "F" | false | "Em7" |
4646
35 | | 3 | "Bb" | "A" | | false | "Em7" |
4747
36 | | 3 | "Bb" | "A" | "F" | false | "Em7" |
48-
37 | "G" | 3 | | | | false | "Em7" |
48+
37 | "G" | 3 | | | | false | "Em7" |
4949
38 | "G" | 3 | | | "F" | false | "Dm7" |
5050
39 | "G" | 3 | | "A" | | false | "F#m7" |
5151
40 | "G" | 3 | | "A" | "F" | false | "Em7" |
@@ -66,6 +66,34 @@ describe('renderChord helper', () => {
6666
const renderedChord = renderChord('Em7', line, song, { renderKey: Key.wrap(renderKey), decapo });
6767
expect(renderedChord).toEqual(outcome);
6868
});
69+
70+
it('respects a higher # rendering key', () => {
71+
const song = new Song();
72+
song.metadata.add('key', 'A');
73+
const renderedChord = renderChord('A', new Line(), song, { renderKey: Key.parse('A#') });
74+
expect(renderedChord).toEqual('A#');
75+
});
76+
77+
it('respects a lower # rendering key', () => {
78+
const song = new Song();
79+
song.metadata.add('key', 'A');
80+
const renderedChord = renderChord('A', new Line(), song, { renderKey: Key.parse('G#') });
81+
expect(renderedChord).toEqual('G#');
82+
});
83+
84+
it('respects a higher b rendering key', () => {
85+
const song = new Song();
86+
song.metadata.add('key', 'A');
87+
const renderedChord = renderChord('A', new Line(), song, { renderKey: Key.parse('Bb') });
88+
expect(renderedChord).toEqual('Bb');
89+
});
90+
91+
it('respects a lower b rendering key', () => {
92+
const song = new Song();
93+
song.metadata.add('key', 'A');
94+
const renderedChord = renderChord('A', new Line(), song, { renderKey: Key.parse('Ab') });
95+
expect(renderedChord).toEqual('Ab');
96+
});
6997
});
7098

7199
describe('chord transposition solfege', () => {
@@ -99,7 +127,7 @@ describe('renderChord helper', () => {
99127
25 | "Sol" | 3 | | "La" | "Fa" | true | "Do#m7" |
100128
26 | "Sol" | 3 | "Sib" | | "Fa" | true | "Sim7" |
101129
27 | "Sol" | 3 | "Sib" | "La" | | true | "Mibm7" |
102-
28 | "Sol" | 3 | "Sib" | "La" | "Fa" | true | "Do#m7" |
130+
28 | "Sol" | 3 | "Sib" | "La" | "Fa" | true | "Rebm7" |
103131
29 | | 3 | | | | false | "Mim7" |
104132
30 | | 3 | | | "Fa" | false | "Mim7" |
105133
31 | | 3 | | "La" | | false | "Mim7" |

0 commit comments

Comments
 (0)