Search in sources :

Example 6 with Fraction

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

the class TimeMapper method getUsedBeats.

/**
 * Gets the used beats in the given measure column.
 * When the given play range starts in this measure, only beats at or after the start beat
 * of the range are returned.
 * When the given play range ends in this measure, only beats at or before the end beat
 * of the given range are returned.
 */
private List<Fraction> getUsedBeats(int iMeasure, Repetition range) {
    // beats where something begins
    val usedBeats = score.getMeasureUsedBeats(iMeasure, true);
    // last beat (where last elements ends)
    usedBeats.add(score.getMeasureFilledBeats(iMeasure));
    List<Fraction> ret = alist(usedBeats.getSize());
    for (val beat : usedBeats) {
        if (iMeasure == range.start.getMeasure()) {
            if (beat.compareTo(range.start.getBeat()) >= 0)
                ret.add(beat);
        } else if (iMeasure == range.end.getMeasure()) {
            if (beat.compareTo(range.end.getBeat()) <= 0)
                ret.add(beat);
        } else {
            ret.add(beat);
        }
    }
    return ret;
}
Also used : lombok.val(lombok.val) Fraction(com.xenoage.utils.math.Fraction)

Example 7 with Fraction

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

the class BarlinesBeatOffsetter method compute.

public Result compute(List<BeatOffset> baseOffsets, ColumnHeader columnHeader, float maxInterlineSpace) {
    ArrayList<BeatOffset> retNotes = alist(baseOffsets);
    ArrayList<BeatOffset> retBarlines = alist();
    // start barline
    retBarlines.add(new BeatOffset(Fraction.Companion.get_0(), 0));
    Barline startBarline = columnHeader.getStartBarline();
    if (startBarline != null && startBarline.getRepeat() == BarlineRepeat.Forward) {
        // forward repeat: move all beats REPEAT_SPACE IS backward
        float move = repeatSpace * maxInterlineSpace;
        for (int i = 0; i < retNotes.size(); i++) {
            BeatOffset oldOffset = retNotes.get(i);
            retNotes.set(i, new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
        }
    }
    // mid-measure barlines
    for (BeatE<Barline> midBarline : columnHeader.getMiddleBarlines()) {
        // get beat of barline, find it in the note offsets and move the following ones
        Fraction beat = midBarline.getBeat();
        int i = 0;
        float move = 0;
        for (; i < retNotes.size(); i++) {
            if (retNotes.get(i).getBeat().compareTo(beat) >= 0) {
                BarlineRepeat repeat = midBarline.getElement().getRepeat();
                if (repeat == BarlineRepeat.Backward) {
                    // backward repeat: additional space before barline
                    move += repeatSpace * maxInterlineSpace;
                    BeatOffset oldOffset = retNotes.get(i);
                    retBarlines.add(new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
                } else if (repeat == BarlineRepeat.Forward) {
                    // forward repeat: additional space after barline
                    BeatOffset oldOffset = retNotes.get(i);
                    retBarlines.add(new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
                    move += repeatSpace * maxInterlineSpace;
                } else if (repeat == BarlineRepeat.Both) {
                    // forward and backward repeat: additional space before and after barline
                    move += repeatSpace * maxInterlineSpace;
                    BeatOffset oldOffset = retNotes.get(i);
                    retBarlines.add(new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
                    move += repeatSpace * maxInterlineSpace;
                } else {
                    retBarlines.add(retNotes.get(i));
                }
                move += midBarlineSpace * maxInterlineSpace;
                break;
            }
        }
        for (; i < retNotes.size(); i++) {
            // move following notes
            BeatOffset oldOffset = retNotes.get(i);
            retNotes.set(i, new BeatOffset(oldOffset.getBeat(), oldOffset.getOffsetMm() + move));
        }
    }
    // end barline
    BeatOffset lastOffset = retNotes.get(retNotes.size() - 1);
    Barline endBarline = columnHeader.getEndBarline();
    if (endBarline != null && endBarline.getRepeat() == BarlineRepeat.Backward) {
        // backward repeat: additional space before end barline
        float move = repeatSpace * maxInterlineSpace;
        retBarlines.add(new BeatOffset(lastOffset.getBeat(), lastOffset.getOffsetMm() + move));
    } else {
        retBarlines.add(lastOffset);
    }
    // return result
    retBarlines.trimToSize();
    return new Result(retNotes, retBarlines);
}
Also used : BarlineRepeat(com.xenoage.zong.core.music.barline.BarlineRepeat) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) Fraction(com.xenoage.utils.math.Fraction) Barline(com.xenoage.zong.core.music.barline.Barline)

Example 8 with Fraction

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

the class VoicesBeatOffsetter method compute.

/**
 * Computes the offsets of all the used beats, including
 * at least beat 0 and the beat at the end of the measure.
 * The beats containing the notes with the lowest valuation
 * (or that needs accidentals) dictate the spacing.
 * See "Ross: The Art of Music Engraving", page 79.
 */
public List<BeatOffset> compute(List<VoiceSpacing> voiceSpacings, Fraction measureBeats, float minimalBeatsOffsetIs) {
    // the list of all used beats of the measure
    // TODO: handle upbeat measures correctly!
    SortedList<Fraction> beats = computeVoicesBeats(voiceSpacings);
    // add final used beat
    beats.add(computeLastBeat(voiceSpacings));
    // add final beat in terms of time signature (only correct for non-upbeat measures)
    beats.add(measureBeats);
    // the resulting offsets for each used beat
    ArrayList<BeatOffset> ret = alist();
    // compute the offset of beat 0
    float offsetMm = getOffsetBeat0InMm(voiceSpacings);
    Fraction lastBeat = Companion.get_0();
    ret.add(new BeatOffset(lastBeat, offsetMm));
    // Otherwise we must find the dominant parts within the voices
    if (voiceSpacings.size() == 1) {
        // only one voice
        float interlineSpace = voiceSpacings.get(0).interlineSpace;
        for (ElementSpacing se : voiceSpacings.get(0).elements) {
            // if last beat offset has same beat, overwrite it
            if (ret.get(ret.size() - 1).getBeat().equals(se.beat))
                ret.remove(ret.size() - 1);
            ret.add(new BeatOffset(se.beat, se.xIs * interlineSpace));
        }
    } else {
        // more than one voice
        // use the following algorithm:
        // for each beat, compute the offset, by asking each voice how much space
        // it requires between the last computed beat offset and the current one.
        // each time, take the greatest distance required.
        Iterator<Fraction> beatsIterator = beats.iterator();
        // ignore beat 0, we have handled it before
        beatsIterator.next();
        while (beatsIterator.hasNext()) {
            Fraction beat = beatsIterator.next();
            // find dominating voice and its minimal required distance
            float minimalDistance = 0;
            for (VoiceSpacing voiceSpacing : voiceSpacings) {
                float interlineSpace = voiceSpacing.interlineSpace;
                float voiceMinimalDistance = computeMinimalDistance(lastBeat, beat, beat.equals(measureBeats), voiceSpacing.voice, voiceSpacing.elements, ret, interlineSpace);
                minimalDistance = Math.max(minimalDistance, voiceMinimalDistance);
                // but we do not want to have different beats at the same offset, so add a small distance.
                if (minimalDistance < minimalBeatsOffsetIs * interlineSpace) {
                    minimalDistance = minimalBeatsOffsetIs * interlineSpace;
                }
            }
            // add beat
            offsetMm += minimalDistance;
            ret.add(new BeatOffset(beat, offsetMm));
            lastBeat = beat;
        }
    }
    ret.trimToSize();
    return ret;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) Fraction(com.xenoage.utils.math.Fraction) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

Example 9 with Fraction

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

the class VoicesBeatOffsetter method computeMinimalDistance.

/**
 * Computes and returns the minimal distance in mm
 * within the given spacing elements of the given voice
 * between the given starting and ending beat (ending beat
 * without its width).
 *
 * Beats may be multiused. The last element with the given start beat
 * and also the last element of the given end beat are used
 * (because the important offset of a beat is the position of the main note
 * or rest, not the position of a grace note or a clef or key signature).
 *
 * If both the starting and ending beat are used,
 * computing their minimal distance is simple.
 *
 * If the ending beat is unused, 0 is returned, since the given
 * voice does not need any space because it has no element to place there.
 *
 * If the starting beat is unused, we have to compute
 * the distance in the following way:
 *
 * The following example shows 2 voices:
 *
 *  #         #         #   ?      { #: there the offsets are already known and given
 * 1/4       1/4       1/4  |
 *                      |   |
 * 1/4       3/8        *  1/8     { this voice is given. *: startBeat is not used
 *                      |   |
 *            startBeat_|   |_endBeat
 *
 * Because startBeat is not used, we compute the distance
 * from the last used beat to the end beat, which is known
 * from the given spacing elements:
 *
 * 1/4       3/8        *  1/8
 *            |_____________|
 *           distanceToEndBeat
 *
 * And we subtract distance between the already computed offset of
 * the last used beat and the also already computed offset of
 * the starting beat (both given in the list of beat offsets):
 *
 * 1/4       3/8        *  1/8
 *            |_________|
 *         distanceToLastUsedBeat
 *
 * The result is the distance between the starting beat
 * and the ending beat:
 *
 * 1/4       3/8        *  1/8
 *                      |___|
 *                     return
 *
 * This value is the minimal distance the given voice needs to
 * place the elements up to the given ending beat.
 */
float computeMinimalDistance(Fraction startBeat, Fraction endBeat, boolean endBeatIsMeasureEnd, Voice voice, List<ElementSpacing> spacings, List<BeatOffset> alreadyComputedBeatOffsets, float interlineSpace) {
    // end beat used? (measure end beat is always used)
    if (endBeatIsMeasureEnd || voice.isBeatUsed(endBeat)) {
        // when measure is incomplete: use last available beat
        if (endBeatIsMeasureEnd) {
            endBeat = voice.getLastUsedBeat(endBeat);
        }
        float endOffset = getLastOffset(spacings, endBeat) * interlineSpace;
        // start beat used?
        if (voice.isBeatUsed(startBeat)) {
            // yes
            float startOffset = getLastOffset(spacings, startBeat) * interlineSpace;
            // return the distance between this two beats
            return endOffset - startOffset;
        } else {
            // no, start beat is not used. use the algorithm described above
            Fraction lastUsedBeat = voice.getLastUsedBeat(startBeat);
            // get offset of the last used beat in the voice spacing
            float lastUsedBeatVoiceSpacingOffset = 0;
            for (ElementSpacing spacing : spacings) {
                if (spacing.beat.equals(lastUsedBeat)) {
                    lastUsedBeatVoiceSpacingOffset = spacing.xIs * interlineSpace;
                    break;
                }
            }
            // compute minimal distance from last used beat to end beat
            float distanceToEndBeat = endOffset - lastUsedBeatVoiceSpacingOffset;
            // get offset of the last computed beat from the list of already computed beat offsets
            float lastComputedBeatOffset = alreadyComputedBeatOffsets.get(alreadyComputedBeatOffsets.size() - 1).getOffsetMm();
            // get offset of the last used beat from the list of already computed beat offsets
            float lastUsedBeatBeatOffsetsOffset = 0;
            for (BeatOffset beatOffset : alreadyComputedBeatOffsets) {
                if (beatOffset.getBeat().equals(lastUsedBeat)) {
                    lastUsedBeatBeatOffsetsOffset = beatOffset.getOffsetMm();
                    break;
                }
            }
            // compute distance between these two offsets
            float distanceToLastUsedBeat = lastComputedBeatOffset - lastUsedBeatBeatOffsetsOffset;
            // return the distance between the last computed beat offset and the end beat
            return distanceToEndBeat - distanceToLastUsedBeat;
        }
    } else {
        // since there is no element, we need no space
        return 0;
    }
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset) Fraction(com.xenoage.utils.math.Fraction)

Example 10 with Fraction

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

the class VoicesBeatOffsetter method computeVoicesBeats.

/**
 * Returns a sorted list of all beats, where
 * chords or rests begin, from the given list of voice spacings.
 * There are no duplicate beats. The ending beats of the voices are not added.
 */
SortedList<Fraction> computeVoicesBeats(List<VoiceSpacing> voiceSpacings) {
    SortedList<Fraction> beats = Companion.sortedListNoDuplicates();
    Fraction beat;
    for (VoiceSpacing voiceSpacing : voiceSpacings) {
        beat = Fraction.Companion.get_0();
        for (ElementSpacing spacingElement : voiceSpacing.elements) {
            MusicElement element = spacingElement.getElement();
            if (element instanceof VoiceElement) {
                // add beat
                beats.add(beat);
                // find the next beat
                beat = beat.add(((VoiceElement) element).getDuration());
            }
        }
    // do not add beat here, because the ending beat of an incomplete measure
    // is not interesting for computing beat offsets.
    }
    return beats;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) MusicElement(com.xenoage.zong.core.music.MusicElement) VoiceElement(com.xenoage.zong.core.music.VoiceElement) Fraction(com.xenoage.utils.math.Fraction) VoiceSpacing(com.xenoage.zong.musiclayout.spacing.VoiceSpacing)

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