package com.xenoage.zong.core.music;
import com.xenoage.utils.annotations.Const;
import com.xenoage.utils.annotations.NonNull;
import com.xenoage.zong.core.music.chord.Accidental;
import com.xenoage.zong.core.music.clef.ClefType;
import com.xenoage.zong.core.music.key.Key;
import com.xenoage.zong.core.music.key.TraditionalKey;
import com.xenoage.zong.core.music.key.TraditionalKey.Mode;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
import static com.xenoage.utils.collections.CollectionUtils.map;
import static java.util.Collections.unmodifiableMap;
/**
* A music context provides information about the musical state
* at a specific position.
*
* These are the current clef, the key signature,
* the list of accidentals and the number of staff lines.
*
* @author Andreas Wenger
*/
@Const @Data @AllArgsConstructor
public class MusicContext {
public static final Map<Pitch, Integer> noAccidentals =
unmodifiableMap(new HashMap<Pitch, Integer>());
public static final MusicContext simpleInstance = new MusicContext(ClefType.clefTreble,
new TraditionalKey(0, Mode.Major), new Pitch[0], 5);
/** The current clef. */
@NonNull private final ClefType clef;
/** The current key signature. */
@NonNull private final Key key;
/** The list of current accidentals (key: pitch without alter, value: alter). */
@NonNull private final Map<Pitch, Integer> accidentals;
/** The number of staff lines. */
private final int linesCount;
/**
* Creates a context with the given clef, key and list of accidentals.
*/
public MusicContext(ClefType clef, Key key, Pitch[] accidentals, int linesCount) {
this.clef = clef;
this.key = key;
Map<Pitch, Integer> accidentalsMap = noAccidentals;
if (accidentals.length > 0) {
accidentalsMap = map();
for (Pitch acc : accidentals)
accidentalsMap.put(acc.withoutAlter(), (int) acc.getAlter());
}
this.accidentals = accidentalsMap;
this.linesCount = linesCount;
}
/**
* Computes and returns the line position of the given pitch,
* that is the vertical offset of the note in half-spaces
* from the bottom line.
*/
public int getLp(Pitch pitch) {
return clef.getLp(pitch);
}
/**
* Gets the accidental for the given pitch.
* When no accidental is needed, null is returned.
*/
public Accidental getAccidental(Pitch pitch) {
//look, if this pitch is already set as an accidental
Integer alter = accidentals.get(pitch.withoutAlter());
if (alter != null) {
if (alter == pitch.getAlter()) {
//we need no accidental
return null;
}
else {
//we need to show the accidental
return Accidental.fromAlter(pitch.getAlter());
}
}
//look, if this alteration is already set in the key signature
if (key.getAlterations()[pitch.getStep()] == pitch.getAlter()) {
//we need no accidental
return null;
}
else {
//we need to show the accidental
return Accidental.fromAlter(pitch.getAlter());
}
}
}