Search in sources :

Example 11 with Fraction

use of com.xenoage.utils.math.Fraction in project Zong by Xenoage.

the class ColumnSpacer method compute.

/**
 * Computes a {@link ColumnSpacing} from a measure column.
 * @param context         the current context, with the current {@link MP} and precomputed
 *                        element {@link Notation}s
 * @param createLeading   true, if a leading spacing has to be created, otherwise false
 * @param notations       the precomputed notations of the measure and voice elements
 */
public ColumnSpacing compute(Context context, boolean createLeading, Notations notations) {
    context.saveMp();
    int measureIndex = context.mp.measure;
    Column column = context.score.getColumn(measureIndex);
    ColumnHeader columnHeader = context.score.getHeader().getColumnHeader(measureIndex);
    // compute the optimal spacings for each voice separately
    List<List<VoiceSpacing>> voiceSpacingsByStaff = alist();
    for (int iStaff : range(column)) {
        List<VoiceSpacing> vss = alist();
        Measure measure = column.get(iStaff);
        for (Voice voice : measure.getVoices()) {
            context.mp = MP.atVoice(iStaff, measureIndex, measure.getVoices().indexOf(voice));
            VoiceSpacing vs = singleVoiceSpacer.compute(context, notations);
            vss.add(vs);
        }
        voiceSpacingsByStaff.add(vss);
    }
    // compute the measure elements (like inner clefs) and accordingly updated voice spacings
    ArrayList<List<ElementSpacing>> optimalMeasureElementsSpacingsByStaff = alist();
    for (int iStaff : range(column)) {
        context.mp = MP.atMeasure(iStaff, measureIndex);
        List<ElementSpacing> measureSpacing = measureElementsSpacer.compute(context, createLeading, voiceSpacingsByStaff.get(iStaff), notations);
        optimalMeasureElementsSpacingsByStaff.add(measureSpacing);
    }
    // compute the common beat offsets of this measure column
    Fraction measureBeats = context.score.getMeasureBeats(measureIndex);
    VoiceSpacingsByStaff voiceSpacings = new VoiceSpacingsByStaff(voiceSpacingsByStaff);
    List<BeatOffset> beatOffsets = voicesBeatOffsetter.compute(voiceSpacings.getAll(), measureBeats, context.settings.offsetBeatsMinimal);
    // recompute beat offsets with respect to barlines
    BarlinesBeatOffsetter.Result offsets = barlinesBeatOffsetter.compute(beatOffsets, columnHeader, context.score.getMaxIS());
    beatOffsets = offsets.voiceElementOffsets;
    List<BeatOffset> barlineOffsets = offsets.barlineOffsets;
    // compute the spacings for the whole column, so that equal beats are aligned
    ArrayList<List<ElementSpacing>> alignedMeasureElementsSpacingsByStaff = alist();
    for (int iStaff : range(column)) {
        Measure measure = column.get(iStaff);
        // voice spacings
        for (int iVoice : range(measure.getVoices())) alignedVoicesSpacer.compute(voiceSpacings.get(iStaff, iVoice), beatOffsets);
        // measure elements, based on the aligned voice spacings
        context.mp = atMeasure(iStaff, measureIndex);
        alignedMeasureElementsSpacingsByStaff.add(measureElementsSpacer.compute(context, createLeading, voiceSpacings.getStaff(iStaff), notations));
    }
    // compute spacings for each staff
    List<MeasureSpacing> measureSpacings = alist(column.size());
    for (int iStaff : range(column)) {
        // create leading spacing, if needed
        LeadingSpacing leadingSpacing = null;
        if (createLeading) {
            context.mp = atBeat(iStaff, measureIndex, 0, Fraction.Companion.get_0());
            leadingSpacing = leadingSpacer.compute(context, notations);
        }
        // create measure spacing
        float interlineSpace = context.score.getInterlineSpace(iStaff);
        measureSpacings.add(new MeasureSpacing(atMeasure(iStaff, measureIndex), interlineSpace, voiceSpacings.getStaff(iStaff), alignedMeasureElementsSpacingsByStaff.get(iStaff), leadingSpacing));
    }
    context.restoreMp();
    return new ColumnSpacing(measureIndex, measureSpacings, beatOffsets, barlineOffsets);
}
Also used : LeadingSpacing(com.xenoage.zong.musiclayout.spacing.LeadingSpacing) ColumnSpacing(com.xenoage.zong.musiclayout.spacing.ColumnSpacing) Fraction(com.xenoage.utils.math.Fraction) ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) ColumnHeader(com.xenoage.zong.core.header.ColumnHeader) Column(com.xenoage.zong.core.music.util.Column) BarlinesBeatOffsetter(com.xenoage.zong.musiclayout.spacer.beat.BarlinesBeatOffsetter) MP.atMeasure(com.xenoage.zong.core.position.MP.atMeasure) Measure(com.xenoage.zong.core.music.Measure) MeasureSpacing(com.xenoage.zong.musiclayout.spacing.MeasureSpacing) VoiceSpacingsByStaff(com.xenoage.zong.musiclayout.layouter.columnspacing.VoiceSpacingsByStaff) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) ArrayList(java.util.ArrayList) List(java.util.List) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing) Voice(com.xenoage.zong.core.music.Voice)

Example 12 with Fraction

use of com.xenoage.utils.math.Fraction in project Zong by Xenoage.

the class MeasureElementsSpacer method compute.

List<ElementSpacing> compute(BeatEList<Clef> clefs, @MaybeEmpty BeatEList<Key> keys, @MaybeNull TimeSignature time, boolean existsLeadingSpacing, List<VoiceSpacing> voiceSpacings, int staff, Notations notations, LayoutSettings layoutSettings) {
    Key key0 = null;
    if (keys.size() > 0 && keys.getFirst().getBeat().equals(Companion.get_0()))
        key0 = keys.getFirst().getElement();
    if (key0 == null && time == null && (clefs == null || clefs.size() == 0)) {
        // nothing to do
        return empty;
    }
    ArrayList<ElementSpacing> ret = alist();
    float startOffset = layoutSettings.offsetMeasureStart;
    // key and time
    // ************
    boolean isKey = !existsLeadingSpacing && key0 instanceof TraditionalKey;
    boolean isTime = time != null;
    if (isKey || isTime) {
        float currentOffset = startOffset;
        // ***
        if (isKey) {
            Notation keyNotation = notations.get(key0, staff);
            ret.add(new SimpleSpacing(keyNotation, Companion.get_0(), startOffset));
            currentOffset += keyNotation.getWidth().getUsedWidth();
        }
        // ****
        if (time != null) {
            Notation timeNotation = notations.get(time, staff);
            ret.add(new SimpleSpacing(timeNotation, Companion.get_0(), currentOffset + timeNotation.getWidth().symbolWidth / 2));
            currentOffset += timeNotation.getWidth().getUsedWidth();
        }
        // move voice elements, if not enough space before first voice element
        ElementSpacing leftSE = getFirstElementSpacing(voiceSpacings);
        if (leftSE != null) {
            float leftSEx = getLeftX(leftSE);
            // existing space
            float ES = leftSEx;
            // additional needed space
            float AS = currentOffset - ES;
            if (AS > 0) {
                shift(voiceSpacings, AS);
                startOffset += AS;
            }
        }
    }
    // voice 2:       1             o
    if (clefs != null) {
        for (BeatE<Clef> ME : clefs) {
            Fraction MEb = ME.getBeat();
            Notation MEnotation = notations.get(ME.getElement());
            float MEwidth = MEnotation.getWidth().getWidth();
            // if there is a leading spacing, ignore elements at beat 0
            if (existsLeadingSpacing && !MEb.isGreater0())
                continue;
            // find VE1 and VE2 for the current element
            ElementSpacing[] ses = getNearestSpacingElements(MEb, voiceSpacings);
            ElementSpacing VE1 = ses[0], VE2 = ses[1];
            // if VE1 is unknown, use startOffset. if VE2 is unknown, ignore this element
            float VE1x = (VE1 != null ? getRightX(VE1) : startOffset);
            if (VE2 == null)
                continue;
            float VE2x = getLeftX(VE2);
            // existing space
            float ES = VE2x - VE1x - 2 * layoutSettings.spacings.widthDistanceMin;
            if (ES < MEwidth) {
                // additional space needed
                float AS = MEwidth - ES;
                // move all elements at or after ME.beat
                VE2x += AS;
                shiftAfterBeat(voiceSpacings, AS, MEb);
            }
            // add measure element
            float MEx = VE2x - layoutSettings.spacings.widthDistanceMin - MEwidth / 2;
            ret.add(new SimpleSpacing(MEnotation, ME.getBeat(), MEx));
        }
    }
    ret.trimToSize();
    return ret;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) SimpleSpacing(com.xenoage.zong.musiclayout.spacing.SimpleSpacing) Clef(com.xenoage.zong.core.music.clef.Clef) Fraction(com.xenoage.utils.math.Fraction) TraditionalKey(com.xenoage.zong.core.music.key.TraditionalKey) Notation(com.xenoage.zong.musiclayout.notation.Notation) TraditionalKey(com.xenoage.zong.core.music.key.TraditionalKey) Key(com.xenoage.zong.core.music.key.Key)

Example 13 with Fraction

use of com.xenoage.utils.math.Fraction in project Zong by Xenoage.

the class SingleVoiceSpacer method compute.

public VoiceSpacing compute(Context context, Notations notations) {
    Voice voice = context.score.getVoice(context.mp);
    float is = context.score.getInterlineSpace(context.mp);
    Fraction measureBeats = context.score.getMeasureBeats(context.mp.measure);
    int staffLinesCount = context.score.getStaff(context.mp).getLinesCount();
    return compute(voice, is, measureBeats, staffLinesCount, notations, context.settings);
}
Also used : Fraction(com.xenoage.utils.math.Fraction) Voice(com.xenoage.zong.core.music.Voice)

Example 14 with Fraction

use of com.xenoage.utils.math.Fraction in project Zong by Xenoage.

the class ChordSpacingsReader method readChordSpacings.

/**
 * Reads the {@link ChordSpacings} from the given {@link XmlReader} at a child element
 * of the "chords" element.
 */
public static ChordSpacings readChordSpacings(XmlReader r) throws IOException {
    HashMap<Fraction, Float> durationWidths = map();
    // load the duration-to-width mapping
    while (r.openNextChildElement()) {
        if (r.getElementName().equals("chord")) {
            // duration format: x/y, e.g. "1/4"
            Fraction duration = Fraction.Companion.fromString(r.getAttributeNotNull("duration"));
            // width format: x+y/z, eg. "3+1/2"
            float width = Fraction.Companion.fromString(r.getAttributeNotNull("width")).toFloat();
            durationWidths.put(duration, width);
        }
        r.closeElement();
    }
    return new ChordSpacings(durationWidths);
}
Also used : ChordSpacings(com.xenoage.zong.musiclayout.settings.ChordSpacings) Fraction(com.xenoage.utils.math.Fraction)

Example 15 with Fraction

use of com.xenoage.utils.math.Fraction in project Zong by Xenoage.

the class MidiConverter method writeVoice.

/**
 * Writes the given voice into the MIDI sequence.
 * @param voiceMp         the staff, measure and voice index
 * @param repetition      the index of the current {@link Repetition}
 */
private void writeVoice(MP voiceMp, int repetition) {
    val voice = score.getVoice(voiceMp);
    for (VoiceElement element : voice.getElements()) {
        // ignore rests. only chords are played
        if (false == MusicElementType.Chord.is(element))
            continue;
        val chord = (Chord) element;
        // grace chords are not supported yet - TODO: ZONG-104: Play grace chords
        if (chord.isGrace())
            continue;
        // start beat of the element
        Fraction duration = chord.getDuration();
        val startBeat = voice.getBeat(chord);
        val rep = repetitions.get(repetition);
        if (false == rep.contains(Companion.time(voiceMp.measure, startBeat)))
            // start beat out of range: ignore element
            continue;
        // MIDI ticks
        val startMidiTime = timeMap.getByRepTime(repetition, Companion.time(voiceMp.measure, startBeat));
        long startTick = startMidiTime.tick;
        long endTick = startTick + durationToTick(duration, resolution);
        long stopTick = endTick;
        if (false == options.midiSettings.durationFactor.equals(Companion.get_1())) {
            // custom duration factor
            stopTick = startTick + round((endTick - startTick) * options.midiSettings.durationFactor.toFloat());
        }
        // play note
        if (startTick < stopTick) {
            float volume = dynamics.getVolumeAt(voiceMp.withBeat(startBeat), repetition);
            int midiVelocity = round(midiMaxValue * volume);
            for (Note note : chord.getNotes()) {
                addNoteToTrack(note.getPitch(), voiceMp.staff, startTick, stopTick, midiVelocity, 0);
            }
        }
    // TODO Timidity doesn't like the following midi events
    /*MetaMessage m = null;
			if (musicelement instanceof Clef)
			{
				Clef c = (Clef) musicelement;
				m = createMidiEvent(c, tracknumber);
			}
			else if (musicelement instanceof NormalTime)
			{
				NormalTime t = (NormalTime) musicelement;
				m = createMidiEvent(t, resolution, tracknumber);
			}
			else if (musicelement instanceof Key)
			{
				Key k = (Key) musicelement;
				m = createMidiEvent(k, tracknumber);
			}
			else if (musicelement instanceof Tempo)
			{
				Tempo tempo = (Tempo)musicelement;
				m = MidiTempoConverter.createMetaMessage(tempo);
			}
			if (m != null)
			{
				MidiEvent event = new MidiEvent(m, starttick);
				track.add(event);
			}*-/
			currenttickinvoice = endtick;
		}*/
    }
}
Also used : lombok.val(lombok.val) Note(com.xenoage.zong.core.music.chord.Note) Fraction(com.xenoage.utils.math.Fraction) Chord(com.xenoage.zong.core.music.chord.Chord)

Aggregations

Fraction (com.xenoage.utils.math.Fraction)32 BeatOffset (com.xenoage.zong.musiclayout.spacing.BeatOffset)6 Chord (com.xenoage.zong.core.music.chord.Chord)5 ElementSpacing (com.xenoage.zong.musiclayout.spacing.ElementSpacing)5 lombok.val (lombok.val)5 Voice (com.xenoage.zong.core.music.Voice)4 Measure (com.xenoage.zong.core.music.Measure)3 VoiceElement (com.xenoage.zong.core.music.VoiceElement)3 Clef (com.xenoage.zong.core.music.clef.Clef)3 TraditionalKey (com.xenoage.zong.core.music.key.TraditionalKey)3 TimeSignature (com.xenoage.zong.core.music.time.TimeSignature)3 VoiceSpacing (com.xenoage.zong.musiclayout.spacing.VoiceSpacing)3 ColumnElementWrite (com.xenoage.zong.commands.core.music.ColumnElementWrite)2 Score (com.xenoage.zong.core.Score)2 Part (com.xenoage.zong.core.music.Part)2 Note (com.xenoage.zong.core.music.chord.Note)2 Rest (com.xenoage.zong.core.music.rest.Rest)2 ChordNotation (com.xenoage.zong.musiclayout.notation.ChordNotation)2 JsonArray (com.google.gson.JsonArray)1 JsonObject (com.google.gson.JsonObject)1