package com.xenoage.zong.commands.core.music; import com.xenoage.utils.document.command.CommandPerformer; import com.xenoage.utils.math.Fraction; import com.xenoage.zong.core.Score; import com.xenoage.zong.core.ScoreFactory; import com.xenoage.zong.core.music.Voice; import com.xenoage.zong.core.music.chord.Chord; import com.xenoage.zong.core.music.chord.Grace; import com.xenoage.zong.core.music.chord.Note; import com.xenoage.zong.core.music.rest.Rest; import com.xenoage.zong.core.music.time.TimeSignature; import com.xenoage.zong.core.music.time.TimeType; import com.xenoage.zong.core.position.MP; import com.xenoage.zong.utils.exceptions.MeasureFullException; import org.junit.Test; import java.util.ArrayList; import java.util.List; import static com.xenoage.utils.kernel.Range.range; import static com.xenoage.utils.math.Fraction.fr; import static com.xenoage.zong.core.music.Pitch.pi; import static com.xenoage.zong.core.music.beam.Beam.beamFromChords; import static com.xenoage.zong.core.position.MP.*; import static org.junit.Assert.*; /** * Tests for {@link VoiceElementWrite}. * * @author Andreas Wenger */ public class VoiceElementWriteTest { /** * Write<pre> * xxxxxxx * into aabbccddeeffgghh * => aaxxxxxxx_ffgghh</pre> */ @Test public void testOverwrite1() { Score score = createTestScoreEighths(); //in voice 1, write a 7/16 rest at 1/8 MP mp = atElement(0, 0, 1, 1); Voice voice = score.getVoice(mp); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, new Rest(fr(7, 16)), null); cmd.execute(); //now, there must be the durations 1/8, 7/16, 1/8, 1/8, 1/8 in voice 1 assertEquals(5, voice.getElements().size()); assertEquals(fr(1, 8), getDur(voice, 0)); assertEquals(fr(7, 16), getDur(voice, 1)); assertEquals(fr(1, 8), getDur(voice, 2)); assertEquals(fr(1, 8), getDur(voice, 3)); assertEquals(fr(1, 8), getDur(voice, 4)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * Write<pre> * xxxxxxxx * into aabbccddeeffgghh * => aaxxxxxxxxffgghh</pre> */ @Test public void testOverwrite2() { Score score = createTestScoreEighths(); //in voice 1, write a half rest at 1/8 MP mp = atElement(0, 0, 1, 1); Voice voice = score.getVoice(mp); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, new Rest(fr(1, 2)), null); cmd.execute(); //now, there must be the durations 1/8, 1/2, 1/8, 1/8, 1/8 in voice 1 assertEquals(5, voice.getElements().size()); assertEquals(fr(1, 8), getDur(voice, 0)); assertEquals(fr(1, 2), getDur(voice, 1)); assertEquals(fr(1, 8), getDur(voice, 2)); assertEquals(fr(1, 8), getDur(voice, 3)); assertEquals(fr(1, 8), getDur(voice, 4)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * Write<pre> * xxxxxxxx * into aabbccddeeffgghh * => xxxxxxxxeeffgghh</pre> */ @Test public void testOverwrite3() { Score score = createTestScoreEighths(); //in voice 1, write a half rest at 0/8 MP mp = atElement(0, 0, 1, 0); Voice voice = score.getVoice(mp); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, new Rest(fr(1, 2)), null); cmd.execute(); //now, there must be the durations 1/2, 1/8, 1/8, 1/8, 1/8 in voice 1 assertEquals(5, voice.getElements().size()); assertEquals(fr(1, 2), getDur(voice, 0)); assertEquals(fr(1, 8), getDur(voice, 1)); assertEquals(fr(1, 8), getDur(voice, 2)); assertEquals(fr(1, 8), getDur(voice, 3)); assertEquals(fr(1, 8), getDur(voice, 4)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * Write<pre> * xxxxxxxx * into aabbccddeeffgghh * => aabbccddxxxxxxxx</pre> */ @Test public void testOverwrite4() { Score score = createTestScoreEighths(); //in voice 1, write a half rest at 4/8 MP mp = atElement(0, 0, 1, 4); Voice voice = score.getVoice(mp); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, new Rest(fr(1, 2)), null); cmd.execute(); //now, there must be the durations 1/8, 1/8, 1/8, 1/8, 1/2 in voice 1 assertEquals(5, voice.getElements().size()); assertEquals(fr(1, 8), getDur(voice, 0)); assertEquals(fr(1, 8), getDur(voice, 1)); assertEquals(fr(1, 8), getDur(voice, 2)); assertEquals(fr(1, 8), getDur(voice, 3)); assertEquals(fr(1, 2), getDur(voice, 4)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * Write<pre> * x * into aabbccddeeffgghh * => x_bbccddeeffgghh</pre> */ @Test public void testOverwrite5() { Score score = createTestScoreEighths(); //in voice 1, write a 1/16 rest at 4/8 MP mp = atElement(0, 0, 1, 0); Voice voice = score.getVoice(mp); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, new Rest(fr(1, 16)), null); cmd.execute(); //now, there must be the durations 1/16, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8 in voice 1 assertEquals(8, voice.getElements().size()); assertEquals(fr(1, 16), getDur(voice, 0)); assertEquals(fr(1, 8), getDur(voice, 1)); assertEquals(fr(1, 8), getDur(voice, 2)); assertEquals(fr(1, 8), getDur(voice, 3)); assertEquals(fr(1, 8), getDur(voice, 4)); assertEquals(fr(1, 8), getDur(voice, 5)); assertEquals(fr(1, 8), getDur(voice, 6)); assertEquals(fr(1, 8), getDur(voice, 7)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * When no collisions happen, elements must stay untouched. */ @Test public void testElementReferences() { Score score = ScoreFactory.create1Staff(); Voice voice = score.getVoice(atVoice(0, 0, 0)); Rest rest0 = new Rest(fr(1, 4)); new VoiceElementWrite(voice, atElement(0, 0, 0, 0), rest0, null).execute(); Rest rest1 = new Rest(fr(1, 4)); new VoiceElementWrite(voice, atElement(0, 0, 0, 1), rest1, null).execute(); Rest rest2 = new Rest(fr(1, 4)); new VoiceElementWrite(voice, atElement(0, 0, 0, 2), rest2, null).execute(); assertTrue(rest0 == voice.getElementAt(fr(0, 4))); assertTrue(rest1 == voice.getElementAt(fr(1, 4))); assertTrue(rest2 == voice.getElementAt(fr(2, 4))); } /** * Writes grace notes in a voice. * <pre> * .. * into aabb__ccddeeffgghh * => aabb..ccddeeffgghh</pre> */ @Test public void testGraceAdd() { Score score = createTestScoreEighths(); CommandPerformer cp = score.getCommandPerformer(); //in voice 1, write two grace notes Voice voice = score.getVoice(atVoice(0, 0, 1)); Chord g1 = grace(1), g2 = grace(2); cp.execute(new VoiceElementWrite(voice, atElement(0, 0, 1, 2), g1, null)); cp.execute(new VoiceElementWrite(voice, atElement(0, 0, 1, 3), g2, null)); //now we must find the other elements unchanged but the grace notes inserted assertEquals(10, voice.getElements().size()); assertEquals(fr(1, 8), getDur(voice, 0)); assertEquals(fr(1, 8), getDur(voice, 1)); assertEquals(fr(0), getDur(voice, 2)); assertEquals(fr(0), getDur(voice, 3)); assertEquals(fr(1, 8), getDur(voice, 4)); assertEquals(fr(1, 8), getDur(voice, 5)); assertEquals(fr(1, 8), getDur(voice, 6)); assertEquals(fr(1, 8), getDur(voice, 7)); assertEquals(fr(1, 8), getDur(voice, 8)); assertEquals(fr(1, 8), getDur(voice, 9)); //right elements? assertEquals(g1, voice.getElement(2)); assertEquals(g2, voice.getElement(3)); //test undo cp.undoMultipleSteps(2); assertTestScoreEighthsOriginalState(score); } /** * Writes elements in a voice that contains grace notes. * <pre> * xxxx * into ..aaaa...bbbb.cccc..</pre> * => ..aaaa...bbbb.cccc..</pre> */ @Test public void testGraceInsert1() { Score score = createTestScoreGraces(); //in voice 0, write a 1/4 rest at position 2 MP mp = atElement(0, 0, 0, 2); Voice voice = score.getVoice(mp); Rest r = new Rest(fr(1, 4)); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, r, null); cmd.execute(); //check notes assertEquals(11, voice.getElements().size()); assertEquals(0, getStep(voice, 0)); //here 1st grace note has step 0, 2nd 1, ... assertEquals(1, getStep(voice, 1)); assertEquals(r, voice.getElement(2)); assertEquals(2, getStep(voice, 3)); assertEquals(3, getStep(voice, 4)); assertEquals(4, getStep(voice, 5)); assertEquals(fr(1, 4), getDur(voice, 6)); assertEquals(5, getStep(voice, 7)); assertEquals(fr(1, 4), getDur(voice, 8)); assertEquals(6, getStep(voice, 9)); assertEquals(0, getStep(voice, 10)); //test undo cmd.undo(); assertTestScoreGracesOriginalState(score); } /** * Writes elements in a voice that contains grace notes. * <pre> * xxxx___xxxx * into ..aaaa...bbbb.cccc.. * => ..xxxx___xxxx.cccc..</pre> */ @Test public void testGraceInsert2() { Score score = createTestScoreGraces(); //in voice 0, write a 1/2 rest at position 2 MP mp = atElement(0, 0, 0, 2); Voice voice = score.getVoice(mp); Rest r = new Rest(fr(1, 2)); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, r, null); cmd.execute(); //check notes assertEquals(7, voice.getElements().size()); assertEquals(0, getStep(voice, 0)); //here 1st grace note has step 0, 2nd 1, ... assertEquals(1, getStep(voice, 1)); assertEquals(r, voice.getElement(2)); assertEquals(5, getStep(voice, 3)); assertEquals(fr(1, 4), getDur(voice, 4)); assertEquals(6, getStep(voice, 5)); assertEquals(0, getStep(voice, 6)); //test undo cmd.undo(); assertTestScoreGracesOriginalState(score); } /** * Writes elements in a voice that contains grace notes. * <pre> * xxxx___xxxx_xx * into ..aaaa...bbbb.cccc.. * => ..xxxx___xxxx_xx__..</pre> */ @Test public void testGraceInsert3() { Score score = createTestScoreGraces(); //in voice 0, write a 5/8 rest at position 2 MP mp = atElement(0, 0, 0, 2); Voice voice = score.getVoice(mp); Rest r = new Rest(fr(5, 8)); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, r, null); cmd.execute(); //check notes assertEquals(5, voice.getElements().size()); assertEquals(0, getStep(voice, 0)); //here 1st grace note has step 0, 2nd 1, ... assertEquals(1, getStep(voice, 1)); assertEquals(r, voice.getElement(2)); assertEquals(6, getStep(voice, 3)); assertEquals(0, getStep(voice, 4)); //test undo cmd.undo(); assertTestScoreGracesOriginalState(score); } /** * Write at a beat.<pre> * xxxxxxx * into aabbccddeeffgghh * => aaxxxxxxx_ffgghh</pre> */ @Test public void testWriteAtBeat() { Score score = createTestScoreEighths(); //in voice 1, write a 7/16 rest at 1/8 MP mp = atBeat(0, 0, 0, fr(1, 8)); Voice voice = score.getVoice(mp); Rest r = new Rest(fr(7, 16)); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, r, null); cmd.execute(); //now, there must be the durations 1/18, 7/16, 1/8, 1/8, 1/8 in voice 1 assertEquals(5, voice.getElements().size()); assertEquals(fr(1, 8), getDur(voice, 0)); assertEquals(fr(7, 16), getDur(voice, 1)); assertEquals(fr(1, 8), getDur(voice, 2)); assertEquals(fr(1, 8), getDur(voice, 3)); assertEquals(fr(1, 8), getDur(voice, 4)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * Write at a beat after the end of the elements.<pre> * xxxx * into aabbccddeeffgghh * => aabbccddeeffgghhrrxxxx</pre> (r is a rest) */ @Test public void testWriteAfterEnd() { Score score = createTestScoreEighths(); //in voice 1, write a 1/4 rest at 9/8 (1/8 after the end) MP mp = atBeat(0, 0, 0, fr(9, 8)); Voice voice = score.getVoice(mp); Rest x = new Rest(fr(1, 4)); VoiceElementWrite cmd = new VoiceElementWrite(voice, mp, x, null); cmd.execute(); //check elements assertEquals(10, voice.getElements().size()); for (int i : range(8)) assertEquals(fr(1, 8), getDur(voice, i)); assertTrue(voice.getElement(8) instanceof Rest); assertEquals(fr(1, 8), getDur(voice, 8)); assertEquals(x, voice.getElement(9)); assertEquals(fr(1, 4), getDur(voice, 9)); //test undo cmd.undo(); assertTestScoreEighthsOriginalState(score); } /** * Obey the time signature. Fail is written element is too long. */ @Test public void testTimeAware() { Score score = ScoreFactory.create1Staff4Measures(); score.getHeader().getColumnHeaders().get(1).setTime(new TimeSignature(TimeType.time_3_4)); //create options VoiceElementWrite.Options options = new VoiceElementWrite.Options(); options.checkTimeSignature = true; //measure 0: senza misura: write 100 1/4 chords Voice voice = score.getVoice(MP.atVoice(0, 0, 0)); for (int i : range(100)) new VoiceElementWrite(voice, atElement(0, 0, 0, i), new Rest(fr(1, 4)), options).execute(); //measure 2: must work for 3/4, then fail for (int i : range(3)) new VoiceElementWrite(voice, atElement(0, 2, 0, i), new Rest(fr(1, 4)), options).execute(); try { new VoiceElementWrite(voice, atElement(0, 2, 0, 3), new Rest(fr(1, 4)), options).execute(); fail(); } catch (MeasureFullException ex) { } } /** * Creates a score with a single staff, a single measure, * two voices with each 8 quarter notes which are beamed. */ private Score createTestScoreEighths() { Score score = ScoreFactory.create1Staff(); new VoiceAdd(score.getMeasure(atMeasure(0, 0)), 1).execute(); for (int iVoice : range(2)) { Voice voice = score.getVoice(atVoice(0, 0, iVoice)); List<Chord> beamChords = new ArrayList<>(); for (int i = 0; i < 8; i++) { Chord chord = new Chord(new Note(pi(0, 0, 4)), fr(1, 8)); //add elements by hand, since the corresonding command is tested itself in this class chord.setParent(voice); voice.getElements().add(chord); beamChords.add(chord); } //create beam beamFromChords(beamChords); } //ensure that assert method works correctly. if not, fail now assertTestScoreEighthsOriginalState(score); return score; } /** * Asserts that the score created by {@link #createTestScoreEighths()} is in its original * state again. Useful after undo. */ private void assertTestScoreEighthsOriginalState(Score score) { for (int iVoice : range(2)) { Voice voice = score.getVoice(atVoice(0, 0, iVoice)); assertEquals(8, voice.getElements().size()); for (int i : range(8)) assertEquals(fr(1, 8), getDur(voice, i)); } } /** * Creates a score with a single staff, one measure and one voice: * <pre>..aaaa...bbbb.cccc..</pre> (. are grace notes, a-c are rests) */ private Score createTestScoreGraces() { Score score = ScoreFactory.create1Staff(); Voice voice = score.getVoice(atVoice(0, 0, 0)); voice.addElement(grace(0)); voice.addElement(grace(1)); voice.addElement(new Rest(fr(1, 4))); voice.addElement(grace(2)); voice.addElement(grace(3)); voice.addElement(grace(4)); voice.addElement(new Rest(fr(1, 4))); voice.addElement(grace(5)); voice.addElement(new Rest(fr(1, 4))); voice.addElement(grace(6)); voice.addElement(grace(0)); //ensure that assert method works correctly. if not, fail now assertTestScoreGracesOriginalState(score); return score; } /** * Asserts that the score created by {@link #createTestScoreGraces()} is in its original * state again. Useful after undo. */ private void assertTestScoreGracesOriginalState(Score score) { Voice voice = score.getVoice(atVoice(0, 0, 0)); assertEquals(11, voice.getElements().size()); assertEquals(0, getStep(voice, 0)); assertEquals(1, getStep(voice, 1)); assertEquals(fr(1, 4), getDur(voice, 2)); assertEquals(2, getStep(voice, 3)); assertEquals(3, getStep(voice, 4)); assertEquals(4, getStep(voice, 5)); assertEquals(fr(1, 4), getDur(voice, 6)); assertEquals(5, getStep(voice, 7)); assertEquals(fr(1, 4), getDur(voice, 8)); assertEquals(6, getStep(voice, 9)); assertEquals(0, getStep(voice, 10)); } private Fraction getDur(Voice voice, int elementIndex) { return voice.getElement(elementIndex).getDuration(); } private int getStep(Voice voice, int elementIndex) { return ((Chord)voice.getElement(elementIndex)).getNotes().get(0).getPitch().getStep(); } private Chord grace(int step) { Chord chord = new Chord(new Note(pi(step, 0)), fr(0, 4)); chord.setGrace(new Grace(true, fr(1, 16))); return chord; } }