Class MSplitter

java.lang.Object
eu.bandm.music.entities.MSplitter

public class MSplitter extends Object
Delivers a notation for rhythms, interpreted in a particular metric structure and notation style. The rhythm is given as sequence of time points or durations, qualified as sounds or pauses. It is assumed to exactly cover the time interval of one measure. The metric structure is given as an MTreeSpec, and the notation style as a set of parameters, realized as instance of MSplitter.CheckedParameters, MTree.CheckedParameters, MCover.CheckedParameters_approximate and MCover.CheckedParameters_divide.
The result includes a sequence of RationalDuration.DottedBaseDuration, representing the note heads, additional beaming information and tuplet brackets.
The calculation includes (a) finding an initial cover, as executed by MCover, and (b) merging these nodes to "dotted notation" or "sibling merge" etc., for stylistic or ergonomic reasons.
Maintains as an internal cache a Trie.
Usage: (a) construct one new instance for any new combination of one MTreeSpec and the Parameter objects. These are: Then (b) for every sequence of durations and sound/pause information, call process(List) / process(List,List). This returns a MSplitter.Result object (possibly from cache). From this, an inner instance of MSplitter.Result.WriteOut must be constructed and the abstract methods overridden to the desired behaviour; finally its MSplitter.Result.WriteOut.process() method is called.
  • Field Details

    • metricTree

      protected final MTree metricTree
      The growing metric tree. Is initially constructed when this instance is constructed, and is modified whenever new divisions must be synthesized.
    • topBracketIsVanishing

      protected final boolean topBracketIsVanishing
      Whether the top-level node has a vanishing bracket.
    • parameters_explicit

      protected final MSplitter.CheckedParameters parameters_explicit
      The parameters controling sibling merge, dotting, syncopas and hemiolas.
    • parameters_divide

      @Opt protected final MCover.CheckedParameters_divide parameters_divide
      Parameters for the "auto-divide" mode.
    • parameters_approximate

      @Opt protected final MCover.CheckedParameters_approximate parameters_approximate
      Parameters for the "binary approximation" mode.
    • cache

      Cache which holds the split result for each particualr sequence of qualified Rationals. (= rational duration and flag whether sound or pause)
    • cacheEvaluator

      protected final Trie.Evaluator<QualifiedRational,MSplitter.Result> cacheEvaluator
      Operator on cache, needed for entering and retrieving cached keys and values.
    • fromNegativeDotting

      protected Set<MTree> fromNegativeDotting
      Stemend for negative dottings are inherited from the right-most merged node and must not be overwritten when summing up the durations of the morged nodes.
    • freeSectionings_rat_sound

      protected final Map<Rational,List<RationalDuration.DottedBaseDuration>> freeSectionings_rat_sound
      Internal cache.
    • freeSectionings_rat_pause

      protected final Map<Rational,List<RationalDuration.DottedBaseDuration>> freeSectionings_rat_pause
    • freeSectionings_int_sound

      protected final Map<Integer,List<Integer>> freeSectionings_int_sound
      Internal cache. See senzatempo 2013120500:
          0 0001  A        0 0001  A       0 0001  A         0 0001  A         
          0 0100  B        0 0100  B       0 1100  B         0 0100  B         
       +  1 0000  C     + 10 0000  C    + 10 0000  C      +100 0000  C         
       ---------            ---------       ---------         ---------            
          1 0101 21       10 0101 37      10 1101 45       100 0101 69         
                                                                                   
          0 0011           0 0011          0 0011            0 0011            
          0 0100           0 0100          0 1100            0 0100            
       +  0 1110        + 01 1110       +  1 1110         + 11 1110            
       ---------        ---------       ---------         ---------            
          1 0101           1 0101         10 1101          100 0101            
                                                                                   
          0 0111  7        0 0111  7       0 1111 15         0 0111  7         
       +  0 1110 14     +  1 1110 30    +  1 1110 30      + 11 1110 62         
       ---------        ---------       ---------         ---------            
          1 0101 21        1 0101 37      10 1101 45       100 0101 69
        
    • freeSectionings_int_pause

      protected final Map<Integer,List<Integer>> freeSectionings_int_pause
  • Constructor Details

  • Method Details

    • get_metricTree

      public MTree get_metricTree()
      Returns the metric tree in its current state, as modified by all preceding metric splitting tasks.
    • process

      public MSplitter.Result process(List<Rational> eventStarts, List<Boolean> isSound)
      Main entry method: process the (ascending) list of start time points and deliver a semantically sensible rendering plan. See process(List).
    • process

      public MSplitter.Result process(List<QualifiedRational> eventStarts)
      Main entry method: process the (ascending) list of start time points and deliver a semantically sensible rendering plan. Processes a list of QualifiedRational (i.e. positive Rational and a Boolean is_sound_not_pause), and finds the list of nodes of the MTree which must be printed to get a minimal representation in note symobles according to some historical standard note writing patterns. These patterns are realized by "Merge Transformations (MX)", which are sibling-merge (MX-S), dotted merge (MX-D), syncopation (XM-Y) and hemiola (MX-H). and called "siblings". The style is determined by the MTreeSpec and the settings in MSplitter.CheckedParameters given when constructing this MSplitter.

      Internally, first an "Initial Coverage" is found, which for each event gives the smallest set of nodes which partition the event's duration. Is automatic mode is supported, new alternative nodes will be constructed if the division factors needed are not yet found in the MTree.

      Output is the set of nodes which shall be printed in the rendering. More precisely: A note symbol shall appear at the start time point of all nodes contained in this set. All nodes which come out of mergings are directly d-writeable (=with one single A note symbol should not appear at the start time point of the nodes which are NOT in the set, but this is not always possible: The algorithm does not check the d-writability of nodes which are NOT the result of merging.

      The duration of each symbol is the distance to the follower in this set (or to the end of the measure, resp.). The nodes which come out of merging correspond to more than one node of the Initial Coverage. For convenience, the calculated duration of the merge result is stored in the result object.

      Any MTree contains already all MTree.EssentialBrackets which are required by non-power-of-two denominators. These are considered by the MetricSplit merging process and be the final write-out. Additionally, nodes NOT merged, i.e. surviving from the Initial Coverage, may be not d-writeable due to the numerator of their duration. This (in the current implementation) will NOT be fixed by additionally inserted proportions (which could be a feasible alternative), but by writing their child nodes or by writing FreeSectionings. The difference must be found out when generating the symbol sequence by comparing the result set with the original tree.

      Some of the merge patterns applied here are "closed", ie. they put a condition on start- and end-point of the event and are applied if and only if they cover the whole event by one single notehead. Others are "open", put only a min condition on the extent, and their result can further be prolongated by tied notes. For instances, classically a syncope cannot be tied to another notehead, but a dotting can.
      Enscribed into the metric tree, the patterns look as follows:

           syncope = MX-Y, a closed pattern.
                     The duration which is exactly as long as the parent metric node, but
                     the event starts later.
                pTop     
            / / /  \ \ \..  toplevel: any division possible
               /    \ 
              p      p2        p and its follower p2 must have identical lengths 
             / \    / \            and div-counts 
                [----[         index of start and follower identical > 0
      
          (will be applied only if the duration of p is d-writable (e.g. for 1/8 or 15/16).
        
      In case of p.div==2, the pattern may stretch over more than one intermediate levels, limited by max_level_syncope_2
            positive dotting = MX-D, an open pattern.
                  pTop     
              /  / \  \
                /   x     identical lengths on all levels
               /   / \    x.div==2  required (but not for pTop.div)
              /       x     maximal dot count is limited by max_dots_positive.
             /       / \    if exceeded, push dots up or down
            [----------[
      
            negative dotting = MX-D, an open pattern.
                   pTop     
                  / \  \  \
                 x   \     identical lengths on all levels
                / \   \    x.div==2  required (but not for pTop.div)
               x       \     maximal dot count is limited by max_dots_negative
              / \       \    if exceeded, push dots up or down
                 [--------]
        
      
            sibling merge = MX-S, an open pattern  
                   p         (p.div >2)
                 / | \
                -  x  x     ident.length, n*x.duration must be d-writeable
                   [----]
                x  -  x
                [----]
      
         
      In all patterns it holds that ...
      • Differences in the duration between siblings (candidates or parents of candidates) are always a barrier.
      • Differences in their division number, i.e. their internal structure, in general are not.
      • Rules for e-writability of the results are complicated (see user documentation and this (German language) article.

      • Most patterns are not applied when the result is not e-writable without furhter proportion brackets.

          FIXME LATER:
          EXCEPT Dottings, which assume such a proportion will be applied when writing out! This requirement is stored in the set MSplitter.Result.additionalProportions, which must be considered when programming any later write-out procedure.
          FIXME hemiole_punct, syncope_punct requires an alternative metric tree, which would be selected automatically when searching for minimal number of symbols:

                         P              2*3*(1/4)
                      /     \
                    P         P
                  / | \     / | \
                 x  x  X.    Xz  z
          
                         P               | 3*2*(1|4)
                      /  |  \
                    P    Q    P
                  / |   /  \  | \
                           /\  
                 x  x  X.    Xz  z
          
                         P               2*2*(1/4)
                      /     \
                    P         P
                  /   \     /   \
                 x     X.    X   z
            
                         P               | 4*1/4
                   /   /   \   \
                           /\
                 x     X.    X   z
            
                         P               | 1/4+(2*1/4)+1/4
                      /  |  \
                    /    Q    \
                  /     / \     \
                 x     X.    X   z
            
                                        etc.
                                        5*2*1/4|1/4+4*2*1/4+1/4
                                        5*2*1/4|1/4+2*1/4+1/4+3*2*1/4|1/2+1/4+2*1/4+1/4+2*1/2|
                                        etc.
            
    • testForSyncopeAndHemiola

      protected boolean testForSyncopeAndHemiola(List<MTree> irep, boolean isSound)
      Returns whether the node list, representing one event, can be replaced by one single symbol, due to syncope or hemiola rules. Is currently NOT called with pauses. Syncopes and hemioles are CLOSED patterns, i.e. only applied when start AND end points match certain criteria. So they can be checked once, before all other patterns, and only the FACT WHETHER they are found must be returned: Iff true, the reaction of the caller is to enter only the first node into the printed set, etc.
      Any denominator !=2^n can be ignored, because it has to be realized by an n-tuplet-notation anyhow (on some higher level).
      Returning true requires that the duration of the new note is e-writable (under the current EB-stack, i.e. only w.r.t. the numerator).
      Parameters:
      irep - the complete initial representation for one event, which shall be simplified
    • splitDots_withBeams

      void splitDots_withBeams(List<MTree> irep, Set<MTree> printThem, Map<MTree,StemEnd> stemEnds, int dotsCount, boolean dotsCountIsMax, int startPos, int endPos, boolean leftNotRight, boolean positiveDotting)
      Add nodes from Initial Coverage to result, representing a sequence of dotted values not exceeding maxDots. The caller guarantees e-printability for the comprehension of (a) "dotCounts+1" nodes exactly, if "dotCountsIsMax==false"; else for all numbers of nodes <= "dotCount+1"
                  negative                   positive 
                   N                        N    i=in Initial Coverage
                 /   \                            /   \        
                N     Xi                         Xi    N 
               / \                            / \      
              N   Xi                        Xi   N     
             / \                                        / \            
            N   Ni                             Ni  X   
           ---][^^^^^^^^^^^]            [^^^^^^^^^]
            
        
      The whole interval of nodes is given by startpos to endpos (INCLUSIVE). LeftNotRight says on which side to begin the grouping, i.e. on which side the most dots will be found if splitting them is necessary.
      LeftNotRight = PositiveDotting XOR parameters.push_dots_down_not_up
      BEAMING:
      pos dots -> nothing necessary.
      neg dots -> shift beams of the last node of every joined group to the first (=representant) of this groupe.
    • parseSiblingsAndDottings

      protected void parseSiblingsAndDottings(List<MTree> irep, boolean isPause, Set<MTree> printThem, Map<MTree,StemEnd> stemEnds)
      Delivers all nodes which shall appear in the notation as a symbol, in the output set argument "printThem". Filters out the subsequent nodes which shall not appear with an own symbol. (As a consequence, their durations must be added to the predecessor when writing out, in some later processing step.)

      Assumes that the input is an Initial Coverage, i.e. adjacent and maximal nodes. Therefore the possible patterns are rather limited, which fact is exploited.
      Assumes that the closed patterns (syncope, hemiola, etc.) have already been tested for, with negatove result.

      The patterns to look for are ...

                       ppp
                /   /     \       \      \
                   n       n+      pp      pp 
                  /  \    /  \    /   \
                                  n     p
                                 / \   / \
                                      n
                 [-------------------]          positive dotting
                 [----------------------]       double pos. dotting   
                 [-------------]                siblings
                         [------------------] 
      
      
                       ppp
                /   /     \       \      \
                  +pp      n       n 
                  /  \    /  \    /  \
                +p    n           
                / \              
               n   n 
                  [--------------]              double neg. dotting  
                  [-----]                       negative dotting
                         [--------------]       siblings
      
      
         Jumps over more than one level imply => no simplification is possible;
      
                          ppp
                /         /     \       \      \
              pp        N        pp
               \       /  \     /   \
                 p             p  
                / \           / \   
                   n         n
                  [---------]
                  [-------------]
       
      In a first step for every node (except the very first) the kind of move is determined:
      • up: from a nephew to an uncle, iff nephew.parent.div==2
      • down: from an uncle to a nephew, iff nephew.parent.div==2
      • horizontal: from sibling to sibling, iff durations identical
      • jump: all others (no pattern ever spans a jump)
      From parent.div==2 and the maximality of the coverage nodes it follows that
      • before "up" there may be no "down" or "horizontal", and
      • after "down" there may be no "up" or "horizontal".
      So the only relevant sequence is
        up-up-..-hori-hori-.. down-down 
      
                     pp
                  /  \  \     \
                /     \  \      \
               p      []  []      p 
              / \    --------     / \
                []  /   hori  \  []
                   /up         \
                                down
                  i0 i1      i2 i3  .... i0 again
        phase       0   1      3    4=flush
      
        i1 = last of neg.dot, first of siblings
        i2 = last of siblings, first of pos.dot 
       
      But there may be more than one such pattern in the same event:
        moves:     up--up--hori--down-down-
                                       hori--down   
                          /     /   \
        initial          P     P     P
          cover:       /  \         /   \ 
                      p            p      p
                     / \               /  |  \
                   p                  q   q   q
                                              /\                     
                                             X
         event:   [----------------------------]
       
      (Not all appearing patterns can have all phases, because after a down can never come an up, because this would imply a gap in the time cover, or a node with only one child.)
    • freeSectioning

      protected List<RationalDuration.DottedBaseDuration> freeSectioning(Rational rat, boolean isSound)
      Delivers a list of RationalDuration.DottedBaseDuration which add up to the given duration value. The calculation of this list is controlled by style parameters, therefore the cache must be maintained specifically, with this MSplitter instanve.
    • freeSectioning

      public List<Integer> freeSectioning(int i, boolean isSound)
      FIXME DOC