package com.xenoage.zong.io.midi.out.dynamics;
import com.xenoage.zong.commands.core.music.ColumnElementWrite;
import com.xenoage.zong.commands.core.music.MeasureAdd;
import com.xenoage.zong.commands.core.music.MeasureElementWrite;
import com.xenoage.zong.commands.core.music.PartAdd;
import com.xenoage.zong.core.Score;
import com.xenoage.zong.core.music.MeasureSide;
import com.xenoage.zong.core.music.Part;
import com.xenoage.zong.core.music.direction.Dynamic;
import com.xenoage.zong.core.music.direction.Wedge;
import com.xenoage.zong.core.music.direction.WedgeEnd;
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.io.midi.out.dynamics.type.FixedDynamics;
import com.xenoage.zong.io.midi.out.dynamics.type.GradientDynamics;
import com.xenoage.zong.io.midi.out.repetitions.RepetitionsFinder;
import com.xenoage.zong.io.selection.Cursor;
import lombok.val;
import org.junit.Test;
import static com.xenoage.utils.math.Fraction.*;
import static com.xenoage.zong.core.music.Pitch.pi;
import static com.xenoage.zong.core.music.barline.Barline.barlineBackwardRepeat;
import static com.xenoage.zong.core.music.barline.Barline.barlineForwardRepeat;
import static com.xenoage.zong.core.music.barline.BarlineStyle.Regular;
import static com.xenoage.zong.core.music.chord.ChordFactory.chord;
import static com.xenoage.zong.core.music.direction.DynamicValue.*;
import static com.xenoage.zong.core.music.direction.DynamicValue.mp;
import static com.xenoage.zong.core.music.direction.WedgeType.Crescendo;
import static com.xenoage.zong.core.music.direction.WedgeType.Diminuendo;
import static com.xenoage.zong.core.position.MP.*;
import static com.xenoage.zong.core.position.Time.time;
import static org.junit.Assert.*;
/**
* Tests for {@link DynamicsFinder}.
*
* @author Andreas Wenger
*/
public class DynamicsFinderTest {
/**
* Tests the following part:
*
* |0 |1 |2 |3 |4 ||
* | |: | :| | ||
* staff 0: measures: mp pp f <<< pp //measure 3+4: f at 2/4, cresc at 3/4 to 1/4 (after that implicitly one level louder = ff), ff at 2/4
* voice 0: ------ ----- ----- -----
* voice 1: mf------------ ----- //not supported yet (TODO (ZONG-100))
* staff 1: measures: ff>>>>>>>>>>mp p
*/
@Test public void testAdvanced() {
val score = getAdvancedScore();
val expected = getAdvancedExpectedPeriods();
val repetitions = RepetitionsFinder.findRepetitions(score);
val actual = DynamicsFinder.findDynamics(score, new DynamicsInterpretation(), repetitions).getPeriods();
assertEquals(expected.toString(), actual.toString());
}
/**
* See {@link #testAdvanced()}.
*/
private Score getAdvancedScore() {
Score score = new Score();
Wedge w;
new PartAdd(score, new Part("", null, 2, null), 0, null).execute();
new MeasureAdd(score, 5).execute();
//time signature and repeats
new ColumnElementWrite(new TimeSignature(TimeType.time_4_4),
score.getColumnHeader(0), null, null ).execute();
new ColumnElementWrite(barlineForwardRepeat(Regular),
score.getColumnHeader(1), null, MeasureSide.Left).execute();
new ColumnElementWrite(barlineBackwardRepeat(Regular, 1),
score.getColumnHeader(2), null, MeasureSide.Right).execute();
//staff 0
new MeasureElementWrite(new Dynamic(mp), score.getMeasure(atMeasure(0, 0)), _0).execute();
new MeasureElementWrite(new Dynamic(pp), score.getMeasure(atMeasure(0, 2)), _0).execute();
new MeasureElementWrite(new Dynamic(f), score.getMeasure(atMeasure(0, 3)), _1$2).execute();
new MeasureElementWrite(w = new Wedge(Crescendo), score.getMeasure(atMeasure(0, 3)), _3$4).execute();
new MeasureElementWrite(new WedgeEnd(w), score.getMeasure(atMeasure(0, 4)), _1$4).execute();
new MeasureElementWrite(new Dynamic(pp), score.getMeasure(atMeasure(0, 4)), _1$2).execute();
//voice 0
val cursor = new Cursor(score, mp0, true);
cursor.write(new Rest(_1));
cursor.write(new Rest(_1));
cursor.write(new Rest(_1));
cursor.write(new Rest(_1));
//voice 1
cursor.setMp(atElement(0, 0, 1, 0));
cursor.write(new Rest(_1$2));
val chord = chord(pi(0, 4), _1$2);
chord.addDirection(new Dynamic(mf));
cursor.write(chord);
cursor.write(new Rest(_1));
cursor.write(new Rest(_1));
cursor.setMp(atElement(0, 4, 1, 0));
cursor.write(new Rest(_1));
//staff 1
new MeasureElementWrite(new Dynamic(ff), score.getMeasure(atMeasure(1, 0)), _0).execute();
new MeasureElementWrite(w = new Wedge(Diminuendo), score.getMeasure(atMeasure(1, 0)), _0).execute();
new MeasureElementWrite(new WedgeEnd(w), score.getMeasure(atMeasure(1, 2)), _0).execute();
new MeasureElementWrite(new Dynamic(mp), score.getMeasure(atMeasure(1, 2)), _0).execute();
new MeasureElementWrite(new Dynamic(p), score.getMeasure(atMeasure(1, 3)), _0).execute();
return score;
}
/**
* See {@link #testAdvanced()}.
*/
private DynamicsPeriods getAdvancedExpectedPeriods() {
val d = new DynamicsPeriodsBuilder();
//staff 0
d.addPeriodToStaff(new DynamicsPeriod(
time(0, _0), time(2, _0), new FixedDynamics(mp)), 0, 0);
d.addPeriodToStaff(new DynamicsPeriod(
time(2, _0), time(3, _0), new FixedDynamics(pp)), 0, 0);
d.addPeriodToStaff(new DynamicsPeriod(
time(1, _0), time(3, _1$2), new FixedDynamics(pp)), 0, 1);
d.addPeriodToStaff(new DynamicsPeriod(
time(3, _1$2), time(3, _3$4), new FixedDynamics(f)), 0, 1);
d.addPeriodToStaff(new DynamicsPeriod(
time(3, _3$4), time(4, _1$4), new GradientDynamics(f, ff)), 0, 1); //ff: implicit target after f-cresc
d.addPeriodToStaff(new DynamicsPeriod(
time(4, _1$4), time(4, _1$2), new FixedDynamics(ff)), 0, 1);
d.addPeriodToStaff(new DynamicsPeriod(
time(4, _1$2), time(5, _0), new FixedDynamics(pp)), 0, 1);
/* TODO: ZONG-100
//voice 1 in staff 0
d.addPeriodToVoice(new DynamicsPeriod(
time(0, _1$2), time(3, _0), new FixedDynamics(mf)), 0, 1, 0); */ //only 1st time; we do not see the mf again
//staff 1
d.addPeriodToStaff(new DynamicsPeriod(
time(0, _0), time(2, _0), new GradientDynamics(ff, mp)), 1, 0);
d.addPeriodToStaff(new DynamicsPeriod(
time(2, _0), time(3, _0), new FixedDynamics(mp)), 1, 0);
d.addPeriodToStaff(new DynamicsPeriod( // f remains, we do not jump in the middle of a cresc
time(1, _0), time(3, _0), new FixedDynamics(mp)), 1, 1);
d.addPeriodToStaff(new DynamicsPeriod(
time(3, _0), time(5, _0), new FixedDynamics(p)), 1, 1);
return d.build();
}
}