Search in sources :

Example 11 with ElementSpacing

use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.

the class AlignedVoicesSpacer method compute.

/**
 * Modifies the given {@link VoiceSpacing}, based on the given beat offsets.
 */
public void compute(VoiceSpacing voiceSpacing, List<BeatOffset> beatOffsets) {
    List<ElementSpacing> spacingElements = voiceSpacing.elements;
    if (spacingElements.size() == 0 || beatOffsets.size() == 0)
        return;
    // find the given beats, that are also used here
    List<BeatOffset> sharedBeats = computeSharedBeats(spacingElements, beatOffsets);
    // interpolate positions between the shared beats
    float lastGivenBeatPosition = 0;
    float lastEndElementPosition = 0;
    int firstElement = 0;
    int lastElement = -1;
    float interlineSpace = voiceSpacing.interlineSpace;
    for (int iGivenBeat : range(sharedBeats)) {
        // for each given beat: find elements before or at that beat
        for (int iElement : range(lastElement + 1, spacingElements.size() - 1)) {
            if (spacingElements.get(iElement).beat.compareTo(sharedBeats.get(iGivenBeat).getBeat()) > 0)
                break;
            lastElement++;
        }
        if (lastElement == -1)
            break;
        // compute horizontal positions and distances of the
        // given beat offsets and the current voice spacing, from the
        // last shared beat up to the current shared beat.
        // we calculate in interline spaces here
        float currentGivenBeatPosition = sharedBeats.get(iGivenBeat).getOffsetMm() / interlineSpace;
        float givenBeatsDistance = currentGivenBeatPosition - lastGivenBeatPosition;
        float currentEndElementPosition = spacingElements.get(lastElement).xIs;
        float elementsDistance = currentEndElementPosition - lastEndElementPosition;
        // interpolate the offsets of the current voice spacing
        // between the last shared beat and the current shared beat.
        // do this in reverse order, because the position of grace notes is dependent
        // on the position of the (following) main note
        float lastOriginalOffsetIs = getLast(beatOffsets).offsetMm / interlineSpace;
        for (int iElement : rangeReverse(lastElement, firstElement)) {
            ElementSpacing e = spacingElements.get(iElement);
            if (false == e.isGrace()) {
                // normal element: interpolate position
                float currentElementOffset = e.xIs - lastEndElementPosition;
                float newElementOffset;
                if (elementsDistance != 0) {
                    // scale offset according to the given beats distance
                    newElementOffset = currentElementOffset / elementsDistance * givenBeatsDistance;
                } else {
                    newElementOffset = currentGivenBeatPosition;
                }
                lastOriginalOffsetIs = e.xIs;
                e.xIs = newElementOffset + lastGivenBeatPosition;
            } else {
                // grace element: same distance to the main note as before
                float distanceBefore = lastOriginalOffsetIs - e.xIs;
                lastOriginalOffsetIs = e.xIs;
                e.xIs = spacingElements.get(iElement + 1).xIs - distanceBefore;
            }
        }
        // next range up to next shared beat
        firstElement = lastElement + 1;
        if (firstElement >= spacingElements.size())
            break;
        lastGivenBeatPosition = currentGivenBeatPosition;
        lastEndElementPosition = currentEndElementPosition;
    }
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) BeatOffset(com.xenoage.zong.musiclayout.spacing.BeatOffset)

Example 12 with ElementSpacing

use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.

the class VoiceStamper method stampVoice.

public List<Stamping> stampVoice(VoiceSpacing voice, float voiceXMm, StaffStampings staffStampings, boolean stampLeadingRests, StamperContext context, // TODO:
FormattedTextStyle defaultLyricStyle, Map<Beam, BeamSpacing> beams, OpenSlursCache openCurvedLinesCache, OpenLyricsCache openLyricsCache, LastLyrics lastLyrics, OpenTupletsCache openTupletsCache) {
    List<Stamping> ret = alist();
    // create the voice elements
    boolean onlyRestsSoFar = true;
    for (ElementSpacing spacingElement : voice.elements) {
        MusicElement element = spacingElement.getElement();
        if (element != null) /* TODO && (stampRests || !(element instanceof Rest)) */
        {
            Notation notation = context.getNotation(element);
            float xMm = voiceXMm + spacingElement.xIs * voice.interlineSpace;
            if (element instanceof Chord) {
                // chord
                onlyRestsSoFar = false;
                Chord chord = (Chord) element;
                BeamSpacing beam = beams.get(chord.getBeam());
                ret.addAll(chordStamper.stampAll((ChordNotation) spacingElement.getNotation(), xMm, beam, staffStampings, context, defaultLyricStyle, openCurvedLinesCache, openLyricsCache, lastLyrics, openTupletsCache));
            } else if (spacingElement instanceof RestSpacing) {
                // rest
                if (false == onlyRestsSoFar || stampLeadingRests) {
                    // not a leading rest, or a leading rest which should be stamped
                    ret.add(elementStamper.createRestStamping((RestSpacing) spacingElement, xMm, context));
                }
            } else {
                throw new IllegalArgumentException("Notation not supported: " + notation);
            }
        }
    }
    return ret;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) Stamping(com.xenoage.zong.musiclayout.stampings.Stamping) BeamSpacing(com.xenoage.zong.musiclayout.spacing.BeamSpacing) ChordNotation(com.xenoage.zong.musiclayout.notation.ChordNotation) MusicElement(com.xenoage.zong.core.music.MusicElement) RestSpacing(com.xenoage.zong.musiclayout.spacing.RestSpacing) Notation(com.xenoage.zong.musiclayout.notation.Notation) ChordNotation(com.xenoage.zong.musiclayout.notation.ChordNotation) Chord(com.xenoage.zong.core.music.chord.Chord)

Example 13 with ElementSpacing

use of com.xenoage.zong.musiclayout.spacing.ElementSpacing in project Zong by Xenoage.

the class MeasureStamper method stampMeasure.

/**
 * Stamps all {@link MeasureElement}s of the given measure (but not the leading elements).
 * @param measureXMm  the horizontal position on the staff in mm, where the measure
 *                    starts (after leading spacing).
 */
public List<Stamping> stampMeasure(MeasureSpacing measure, float measureXMm, StamperContext context) {
    List<Stamping> ret = alist();
    for (ElementSpacing element : measure.elements) {
        MusicElement me = element.getElement();
        if (me != null) {
            Notation notation = context.getNotation(me);
            float xMm = measureXMm + element.xIs * measure.interlineSpace;
            ret.add(stamp(notation, xMm, context));
        }
    }
    return ret;
}
Also used : ElementSpacing(com.xenoage.zong.musiclayout.spacing.ElementSpacing) Stamping(com.xenoage.zong.musiclayout.stampings.Stamping) MusicElement(com.xenoage.zong.core.music.MusicElement) Notation(com.xenoage.zong.musiclayout.notation.Notation) ClefNotation(com.xenoage.zong.musiclayout.notation.ClefNotation) TimeNotation(com.xenoage.zong.musiclayout.notation.TimeNotation) TraditionalKeyNotation(com.xenoage.zong.musiclayout.notation.TraditionalKeyNotation)

Example 14 with ElementSpacing

use of com.xenoage.zong.musiclayout.spacing.ElementSpacing 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 15 with ElementSpacing

use of com.xenoage.zong.musiclayout.spacing.ElementSpacing 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 = sortedListNoDuplicates();
    Fraction beat;
    for (VoiceSpacing voiceSpacing : voiceSpacings) {
        beat = Fraction._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

ElementSpacing (com.xenoage.zong.musiclayout.spacing.ElementSpacing)26 VoiceSpacing (com.xenoage.zong.musiclayout.spacing.VoiceSpacing)17 Test (org.junit.Test)11 BeatOffset (com.xenoage.zong.musiclayout.spacing.BeatOffset)10 ElementWidth (com.xenoage.zong.musiclayout.spacing.ElementWidth)7 Fraction (com.xenoage.utils.math.Fraction)6 VoiceElement (com.xenoage.zong.core.music.VoiceElement)6 Voice (com.xenoage.zong.core.music.Voice)5 Clef (com.xenoage.zong.core.music.clef.Clef)5 ChordNotation (com.xenoage.zong.musiclayout.notation.ChordNotation)5 Notation (com.xenoage.zong.musiclayout.notation.Notation)5 RestNotation (com.xenoage.zong.musiclayout.notation.RestNotation)5 MusicElement (com.xenoage.zong.core.music.MusicElement)4 VoiceTest (com.xenoage.zong.core.music.VoiceTest)4 ClefNotation (com.xenoage.zong.musiclayout.notation.ClefNotation)4 Notations (com.xenoage.zong.musiclayout.notation.Notations)4 LayoutSettingsTest (com.xenoage.zong.musiclayout.settings.LayoutSettingsTest)4 LeadingSpacing (com.xenoage.zong.musiclayout.spacing.LeadingSpacing)4 TraditionalKeyNotation (com.xenoage.zong.musiclayout.notation.TraditionalKeyNotation)3 Stamping (com.xenoage.zong.musiclayout.stampings.Stamping)3