package com.xenoage.zong.io.musicxml.in.readers; import com.xenoage.utils.annotations.MaybeNull; import com.xenoage.utils.math.Fraction; import com.xenoage.zong.core.music.ColumnElement; import com.xenoage.zong.core.music.direction.*; import com.xenoage.zong.core.music.direction.Pedal.Type; import com.xenoage.zong.core.music.format.Positioning; import com.xenoage.zong.core.music.util.DurationInfo; import com.xenoage.zong.io.musicxml.in.util.StaffDetails; import com.xenoage.zong.musicxml.types.*; import com.xenoage.zong.musicxml.types.choice.MxlDirectionTypeContent; import com.xenoage.zong.musicxml.types.choice.MxlDirectionTypeContent.MxlDirectionTypeContentType; import lombok.RequiredArgsConstructor; import static com.xenoage.utils.NullUtils.notNull; import static com.xenoage.utils.math.Fraction.fr; /** * Reads a {@link Direction} from a {@link MxlDirection}. * * @author Andreas Wenger */ @RequiredArgsConstructor public class DirectionReader { private final MxlDirection mxlDirection; private MxlDirectionTypeContent currentMxlDirType; private int staff; private StaffDetails staffDetails; //TODO private FontInfo defaultFont; //TODO: add support for multiple direction-types within a single MusicXML direction public void readToContext(Context context) { //staff staff = notNull(mxlDirection.getStaff(), 1) - 1; staffDetails = StaffDetails.fromContext(context, staff); //TODO defaultFont = context.getScore().getFormat().lyricFont; //direction-types Direction direction = null; for (MxlDirectionType mxlType : mxlDirection.getDirectionTypes()) { currentMxlDirType = mxlType.getContent(); MxlDirectionTypeContentType mxlDTCType = currentMxlDirType.getDirectionTypeContentType(); switch (mxlDTCType) { case Coda: { Coda coda = readCoda(); context.writeColumnElement(coda); break; } case Dynamics: { Dynamic dynamics = readDynamics(); context.writeMeasureElement(dynamics, staff); break; } case Metronome: { direction = readMetronome(); break; } case Pedal: { Pedal pedal = readPedal(); if (pedal != null) context.writeMeasureElement(pedal, staff); break; } case Segno: { Segno segno = readSegno(); context.writeColumnElement(segno); break; } case Wedge: { //wedge readWedgeToContext(context); break; } case Words: { //words (currently only one element is supported) if (direction == null) direction = readWords(context.getSettings().getTextReader()); break; } default: } } try { direction = readSound(direction); } catch (Exception ex) { context.reportError(ex.getMessage()); } //write direction to score //TODO: find out if measure direction or column direction. //currently, we write a column element only for tempo or navigation markers if (direction != null) { if (direction instanceof Tempo || direction instanceof NavigationSign) context.writeColumnElement((ColumnElement) direction); else context.writeMeasureElement(direction, staff); } } private Coda readCoda() { Coda coda = new Coda(); coda.setPositioning(readPositioning()); return coda; } private Dynamic readDynamics() { MxlDynamics mxlDynamics = (MxlDynamics) currentMxlDirType; DynamicValue type = mxlDynamics.getElement(); Dynamic dynamics = new Dynamic(type); dynamics.setPositioning(readPositioning()); return dynamics; } private Direction readMetronome() { MxlMetronome mxlMetronome = (MxlMetronome) currentMxlDirType; //compute base beat Fraction baseBeat = mxlMetronome.getBeatUnit().getDuration(); baseBeat = DurationInfo.getDuration(baseBeat, mxlMetronome.getDotsCount()); Tempo tempo = new Tempo(baseBeat, mxlMetronome.getPerMinute()); //text: TODO //TODO tempo.setFont(FontInfoReader.read(currentMxlDirection, defaultFont)); tempo.setPositioning(readPositioning()); return tempo; } @MaybeNull private Pedal readPedal() { MxlPedal mxlPedal = (MxlPedal) currentMxlDirType; Pedal.Type type = null; switch (mxlPedal.getType()) { case Start: type = Type.Start; break; case Stop: type = Type.Stop; break; default: return null; } Pedal pedal = new Pedal(type); pedal.setPositioning(readPositioning()); return pedal; } private Segno readSegno() { Segno segno = new Segno(); segno.setPositioning(readPositioning()); return segno; } private void readWedgeToContext(Context context) { MxlWedge mxlWedge = (MxlWedge) currentMxlDirType; int number = mxlWedge.getNumber(); switch (mxlWedge.getType()) { case Crescendo: Wedge crescendo = new Wedge(WedgeType.Crescendo); crescendo.setPositioning(readPositioning()); context.writeMeasureElement(crescendo, staff); context.openWedge(number, crescendo); break; case Diminuendo: Wedge diminuendo = new Wedge(WedgeType.Diminuendo); diminuendo.setPositioning(readPositioning()); context.writeMeasureElement(diminuendo, staff); context.openWedge(number, diminuendo); break; case Stop: Wedge wedge = context.closeWedge(number); boolean err = false; if (wedge == null) { err = true; context.reportError("Wedge " + (number + 1) + " is not open"); } //the stop must be in the same staff and after the beginning of the wedge else if (staff != wedge.getMP().staff || context.getMp().compareTimeTo(wedge.getMP()) <= 0) { err = true; context.reportError("Wedge " + (number + 1) + " does not end at a valid MP"); } if (err) { //in case of an error, remove the starting element if (wedge != null) context.removeUnclosedWedge(wedge); } else { context.writeMeasureElement(wedge.getWedgeEnd(), staff); } break; } } @MaybeNull private Words readWords(TextReader textReader) { MxlWords mxlWords = (MxlWords) currentMxlDirType; MxlFormattedText mxlFormattedText = mxlWords.getFormattedText(); if (mxlFormattedText.getValue().length() == 0) return null; Words words = new Words(textReader.readText(mxlFormattedText)); words.setPositioning(readPositioning()); //TODO FontInfo fontInfo = FontInfoReader.read(currentMxlDirection, defaultFont); //TODO words.setFont(fontInfo); return words; } private Positioning readPositioning() { return new PositioningReader(staffDetails).readFromAny(currentMxlDirType, mxlDirection); } private Direction readSound(Direction direction) { //sound for words: tempo MxlSound mxlSound = mxlDirection.getSound(); if (mxlSound != null && mxlSound.getTempo() != null && direction instanceof Words) { Words words = (Words) direction; //always expressed in quarter notes per minute int quarterNotesPerMinute = mxlSound.getTempo().intValue(); //convert words into a tempo direction direction = new Tempo(fr(1, 4), quarterNotesPerMinute); //TODO: words.getText() //direction.setFontInfo(words.getFontInfo()); //TODO direction.setPositioning(words.getPositioning()); } return direction; } }