Skip to content

Commit c456a11

Browse files
authored
Merge pull request #1545 from rvilarl/feature/unisonConfigurable
unison configurable
2 parents c71cf53 + a376503 commit c456a11

File tree

4 files changed

+115
-28
lines changed

4 files changed

+115
-28
lines changed

src/stavenote.ts

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -231,23 +231,28 @@ export class StaveNote extends StemmableNote {
231231
//If we are sharing a line and in the same voice, only then offset one note
232232
const lineDiff = Math.abs(noteU.line - noteL.line);
233233
if (noteU.note.hasStem() && noteL.note.hasStem()) {
234-
//If we have different dot values, must offset
235-
//Or If we have a white mixed with a black notehead, must offset
236-
let whiteNoteHeadCount = 0;
237-
let blackNoteHeadCount = 0;
238-
if (Tables.durationToNumber(noteU.note.duration) === 2) {
239-
whiteNoteHeadCount++;
240-
} else if (Tables.durationToNumber(noteU.note.duration) > 2) {
241-
blackNoteHeadCount++;
242-
}
243-
if (Tables.durationToNumber(noteL.note.duration) === 2) {
244-
whiteNoteHeadCount++;
245-
} else if (Tables.durationToNumber(noteL.note.duration) > 2) {
246-
blackNoteHeadCount++;
247-
}
234+
const noteUHead = Tables.codeNoteHead(
235+
noteU.note.sortedKeyProps[0].keyProps.code ?? 'N',
236+
noteU.note.duration
237+
);
238+
const noteLHead = Tables.codeNoteHead(
239+
noteL.note.sortedKeyProps[noteL.note.sortedKeyProps.length - 1].keyProps.code ?? 'N',
240+
noteL.note.duration
241+
);
248242
if (
249-
(whiteNoteHeadCount !== 2 && blackNoteHeadCount !== 2) ||
250-
noteU.note.getModifiersByType(Category.Dot).length !== noteL.note.getModifiersByType(Category.Dot).length
243+
// If unison is not configured, shift
244+
!Tables.UNISON ||
245+
// If we have different noteheads, shift
246+
noteUHead !== noteLHead ||
247+
// If we have different dot values, shift
248+
noteU.note.getModifiers().filter((item) => item.getCategory() === Category.Dot && item.getIndex() === 0)
249+
.length !==
250+
noteL.note.getModifiers().filter((item) => item.getCategory() === Category.Dot && item.getIndex() === 0)
251+
.length ||
252+
// If the notes are quite close but not on the same line, shift
253+
(lineDiff < 1 && lineDiff > 0) ||
254+
// If styles are different, shift
255+
JSON.stringify(noteU.note.getStyle()) !== JSON.stringify(noteL.note.getStyle())
251256
) {
252257
xShift = voiceXShift + 2;
253258
if (noteU.stemDirection === noteL.stemDirection) {
@@ -257,16 +262,6 @@ export class StaveNote extends StemmableNote {
257262
// shift lower voice right
258263
noteL.note.setXShift(xShift);
259264
}
260-
} else if (lineDiff < 1 && lineDiff > 0) {
261-
//if the notes are quite close but not on the same line, shift
262-
xShift = voiceXShift + 2;
263-
if (noteU.stemDirection === noteL.stemDirection) {
264-
// upper voice is middle voice, so shift it right
265-
noteU.note.setXShift(xShift);
266-
} else {
267-
// shift lower voice right
268-
noteL.note.setXShift(xShift);
269-
}
270265
} else if (noteU.note.voice !== noteL.note.voice) {
271266
//If we are not in the same voice
272267
if (noteU.stemDirection === noteL.stemDirection) {
@@ -284,8 +279,8 @@ export class StaveNote extends StemmableNote {
284279
} //Very close whole notes
285280
} else if (lineDiff < 1) {
286281
xShift = voiceXShift + 2;
287-
if (noteU.stemDirection === noteL.stemDirection) {
288-
// upper voice is middle voice, so shift it right
282+
if (noteU.note.duration < noteL.note.duration) {
283+
// upper voice is shorter, so shift it right
289284
noteU.note.setXShift(xShift);
290285
} else {
291286
// shift lower voice right

src/tables.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ const ornaments: Record<string, { code: string }> = {
539539
};
540540

541541
export class Tables {
542+
static UNISON = true;
542543
static SOFTMAX_FACTOR = 10;
543544
static STEM_WIDTH = 1.5;
544545
static STEM_HEIGHT = 35;

tests/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export * from './tremolo_tests';
8080
export * from './tuning_tests';
8181
export * from './tuplet_tests';
8282
export * from './typeguard_tests';
83+
export * from './unison_tests';
8384
export * from './vf_prefix_tests';
8485
export * from './vibrato_tests';
8586
export * from './vibratobracket_tests';

tests/unison_tests.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// [VexFlow](https://vexflow.com) - Copyright (c) Mohit Muthanna 2010.
2+
// MIT License
3+
//
4+
// Unison Tests
5+
import { TestOptions, VexFlowTests } from './vexflow_test_helpers';
6+
7+
import { Tables } from '../src/tables';
8+
9+
const UnisonTests = {
10+
Start(): void {
11+
QUnit.module('Unison');
12+
const run = VexFlowTests.runTests;
13+
run('Simple(true)', simple, { unison: true, voice1: 'e4/q, e4/q, e4/h', voice2: 'e4/8, e4/8, e4/q, e4/h' });
14+
run('Simple(false)', simple, { unison: false, voice1: 'e4/q, e4/q, e4/h', voice2: 'e4/8, e4/8, e4/q, e4/h' });
15+
run('Accidentals(true)', simple, {
16+
unison: true,
17+
voice1: 'e4/q, e#4/q, e#4/h',
18+
voice2: 'e4/8, e4/8, eb4/q, eb4/h',
19+
});
20+
run('Accidentals(false)', simple, {
21+
unison: false,
22+
voice1: 'e4/q, e#4/q, e#4/h',
23+
voice2: 'e4/8, e4/8, eb4/q, eb4/h',
24+
});
25+
run('Dots(true)', simple, { unison: true, voice1: 'e4/q.., e4/16, e4/h', voice2: '(a4 e4)/q., e4/8, e4/h' });
26+
run('Dots(false)', simple, { unison: false, voice1: 'e4/q.., e4/16, e4/h', voice2: '(a4 e4)/q., e4/8, e4/h' });
27+
run('Breve(true)', breve, { unison: true });
28+
run('Breve(false)', breve, { unison: false });
29+
run('Style(true)', style, { unison: true });
30+
run('Style(false)', style, { unison: false });
31+
},
32+
};
33+
34+
function simple(options: TestOptions): void {
35+
Tables.UNISON = options.params.unison;
36+
const vf = VexFlowTests.makeFactory(options, 500, 200);
37+
const score = vf.EasyScore();
38+
39+
const system = vf.System({ y: 40, x: 10, width: 400 });
40+
system.addStave({
41+
voices: [score.voice(score.notes(options.params.voice1)), score.voice(score.notes(options.params.voice2))],
42+
});
43+
44+
system.getStaves()[0].setClef('treble');
45+
system.getStaves()[0].setTimeSignature('4/4');
46+
vf.draw();
47+
expect(0);
48+
}
49+
50+
function style(options: TestOptions): void {
51+
Tables.UNISON = options.params.unison;
52+
const vf = VexFlowTests.makeFactory(options, 500, 200);
53+
const score = vf.EasyScore();
54+
55+
const system = vf.System({ y: 40, x: 10, width: 400 });
56+
const notes1 = score.notes('e4/q, e4/q, e4/h');
57+
const notes2 = score.notes('e4/8, e4/8, e4/q, e4/h');
58+
notes1[2].setStyle({ fillStyle: 'blue', strokeStyle: 'blue' });
59+
notes2[3].setStyle({ fillStyle: 'green', strokeStyle: 'green' });
60+
system.addStave({
61+
voices: [score.voice(notes1), score.voice(notes2)],
62+
});
63+
64+
system.getStaves()[0].setClef('treble');
65+
system.getStaves()[0].setTimeSignature('4/4');
66+
vf.draw();
67+
expect(0);
68+
}
69+
70+
function breve(options: TestOptions): void {
71+
Tables.UNISON = options.params.unison;
72+
const vf = VexFlowTests.makeFactory(options, 500, 200);
73+
const score = vf.EasyScore();
74+
75+
const system = vf.System({ y: 40, x: 10, width: 400 });
76+
system.addStave({
77+
voices: [
78+
score.voice([vf.StaveNote({ keys: ['e/4'], duration: '1/2' })], { time: '8/4' }),
79+
score.voice(score.notes('e4/1, e4/1'), { time: '8/4' }),
80+
],
81+
});
82+
83+
system.getStaves()[0].setClef('treble');
84+
system.getStaves()[0].setTimeSignature('8/4');
85+
vf.draw();
86+
expect(0);
87+
}
88+
89+
VexFlowTests.register(UnisonTests);
90+
export { UnisonTests };

0 commit comments

Comments
 (0)