]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-spanner.cc
''
[lilypond.git] / lily / spacing-spanner.cc
1 /*   
2   spacing-spanner.cc -- implement Spacing_spanner
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 1999--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include <math.h>
11 #include <stdio.h>
12
13 #include "line-of-score.hh"
14 #include "paper-def.hh"
15 #include "paper-score.hh"
16 #include "paper-column.hh"
17 #include "item.hh"
18 #include "moment.hh"
19 #include "note-spacing.hh"
20 #include "misc.hh"
21 #include "warn.hh"
22 #include "staff-spacing.hh"
23 #include "spring.hh"
24 #include "paper-column.hh"
25 #include "spaceable-grob.hh"
26
27 /*
28   paper-column:
29
30   Don't be confused by right-items: each spacing wish can also contain
31   a number of items, with which a spacing constraint may be kept. It's
32   a little baroque, but it might come in handy later on?
33     
34  */
35 class Spacing_spanner
36 {
37 public:
38   static void standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
39                                                  Real * fixed, Real * space, Moment);
40   
41
42   static Real default_bar_spacing (Grob*,Grob*,Grob*,Moment);
43   static Real note_spacing (Grob*,Grob*,Grob*,Moment, bool*);
44   static Real get_duration_space (Grob*,Moment dur, Rational shortest, bool*);
45   static Rational find_shortest (Link_array<Grob> const &);  
46   static void breakable_column_spacing (Grob*, Item* l, Item *r, Moment);
47   static void find_loose_columns () {}
48   static void prune_loose_colunms (Grob*,Link_array<Grob> *cols, Rational);
49   static void find_loose_columns (Link_array<Grob> cols);
50   static void set_explicit_neighbor_columns (Link_array<Grob> cols);
51   static void set_implicit_neighbor_columns (Link_array<Grob> cols);
52   static void do_measure (Rational, Grob*me,Link_array<Grob> *cols);
53   static void musical_column_spacing (Grob*,Item*,Item*, Real, Rational); 
54   DECLARE_SCHEME_CALLBACK (set_springs, (SCM ));
55 };
56
57 /*
58   Return whether COL is fixed to its neighbors by some kind of spacing
59   constraint.
60 */
61 static bool
62 loose_column (Grob *l, Grob *c, Grob *r) 
63 {
64   SCM rns = c->get_grob_property ("right-neighbors");
65   SCM lns = c->get_grob_property ("left-neighbors");
66
67  /*
68     If this column doesn't have a proper neighbor, we should really
69     make it loose, but spacing it correctly is more than we can
70     currently can handle.
71
72     (this happens in the following situation:
73
74        |
75        |    clef G 
76       *
77
78        |               |      ||
79        |               |      || 
80       O               O       ||
81
82
83     the column containing the clef is really loose, and should be
84     attached right to the first column, but that is a lot of work for
85     such a borderline case.)
86     
87   */  
88   if (!gh_pair_p (lns) || !gh_pair_p (rns))
89     return false;
90
91   Item * l_neighbor = dynamic_cast<Item*>  (unsmob_grob (gh_car (lns)));
92   Item * r_neighbor = dynamic_cast<Item*>  (unsmob_grob (gh_car (rns)));
93
94   if (!l_neighbor || !r_neighbor)
95     return false;
96
97   l_neighbor = l_neighbor->column_l();
98   r_neighbor = dynamic_cast<Item*> (Note_spacing::right_column  (r_neighbor));
99
100   if (l == l_neighbor && r == r_neighbor)
101     return false;
102
103   if (!l_neighbor || !r_neighbor)
104     return false;
105
106   /*
107     Only declare loose if the bounds make a little sense.  This means
108     some cases (two isolated, consecutive clef changes) won't be
109     nicely folded, but hey, then don't do that.
110   */
111   if ((Paper_column::musical_b (l_neighbor) || Item::breakable_b (l_neighbor))
112       && (Paper_column::musical_b (r_neighbor) || Item::breakable_b (r_neighbor)))
113     {
114       return true;
115     }
116
117
118   /*
119     If in doubt: we're not loose; the spacing engine should space for
120     it, risking suboptimal spacing.
121
122     (Otherwise, we might risk core dumps, and other weird stuff.)
123
124   */
125   return false;
126 }
127
128 /*
129   Remove columns that are not tightly fitting from COLS. In the
130   removed columns, set 'between-cols to the columns where it is in
131   between.
132 */
133 void
134 Spacing_spanner::prune_loose_colunms (Grob*me,Link_array<Grob> *cols, Rational shortest)
135 {
136   Link_array<Grob> newcols;
137   Real increment = gh_scm2double (me->get_grob_property ("spacing-increment"));
138   for (int i=0; i < cols->size ();  i++)
139     {
140       if (Item::breakable_b (cols->elem(i)) || Paper_column::musical_b (cols->elem (i)))
141         {
142           newcols.push (cols->elem(i));
143           continue;
144         }
145
146       Grob *c = cols->elem(i);
147       if (loose_column (cols->elem (i-1), c, cols->elem (i+1)))
148         {
149           SCM lns = c->get_grob_property ("left-neighbors");
150           lns = gh_pair_p (lns) ? gh_car (lns) : SCM_BOOL_F;
151
152           SCM rns = c->get_grob_property ("right-neighbors");
153           rns = gh_pair_p (rns) ? gh_car (rns) : SCM_BOOL_F;
154
155           /*
156             Either object can be non existent, if the score ends
157             prematurely.
158            */
159           rns = gh_car (unsmob_grob (rns)->get_grob_property ("right-items"));
160           c->set_grob_property ("between-cols", gh_cons (lns,
161                                                          rns));
162
163           /*
164             Set distance constraints for loose columns
165           */
166           Drul_array<Grob*> next_door;
167           next_door[LEFT] =cols->elem (i - 1);
168           next_door[RIGHT] =cols->elem (i + 1);   
169           Direction d = LEFT;
170           Drul_array<Real> dists(0,0);
171
172           do
173             {
174               dists[d] = 0.0;
175               Item *lc = dynamic_cast<Item*> ((d == LEFT)  ? next_door[LEFT] : c);
176               Item *rc = dynamic_cast<Item*> (d == LEFT  ? c : next_door[RIGHT]);
177
178               for (SCM s = lc->get_grob_property ("spacing-wishes");
179                    gh_pair_p (s); s = gh_cdr (s))
180                 {
181                   Grob *sp = unsmob_grob (gh_car (s));
182                   if (Note_spacing::left_column (sp) != lc
183                       || Note_spacing::right_column (sp) != rc)
184                     continue;
185
186                   Real space, fixed;
187                   fixed = 0.0;
188                   bool dummy;
189
190                   if (d == LEFT)
191                     {
192                       /*
193                         The note spacing should be taken from the musical
194                         columns.
195                     
196                       */
197                       Real base = note_spacing (me, lc, rc, shortest, &dummy);
198                       Note_spacing::get_spacing (sp, rc, base, increment, &space, &fixed);
199
200                       space -= increment; 
201                   
202                       dists[d] = dists[d] >? space;
203                     }
204                   else
205                     {
206                       Real space, fixed_space;
207                       Staff_spacing::get_spacing_params (sp,
208                                                          &space, &fixed_space);
209
210                       dists[d] = dists[d] >? fixed_space;
211                     }
212                   
213                 }
214             }
215           while (flip (&d) != LEFT);
216
217           Rod r;
218           r.distance_f_ = dists[LEFT] + dists[RIGHT];
219           r.item_l_drul_[LEFT] = dynamic_cast<Item*> (cols->elem(i-1));
220           r.item_l_drul_[RIGHT] = dynamic_cast<Item*> (cols->elem (i+1));
221
222           r.add_to_cols ();
223         }
224       else
225         {
226           newcols.push (c);
227         }
228     }
229
230   *cols = newcols;
231 }
232
233 /*
234   Set neighboring columns determined by the spacing-wishes grob property.  
235 */
236 void
237 Spacing_spanner::set_explicit_neighbor_columns (Link_array<Grob> cols)
238 {
239   for (int i=0; i < cols.size(); i++)
240     {
241       SCM right_neighbors = SCM_EOL;
242       int min_rank = 100000;    // inf.
243
244
245       SCM wishes=  cols[i]->get_grob_property ("spacing-wishes");
246       for (SCM s =wishes; gh_pair_p (s); s = gh_cdr (s))
247         {
248           Item * wish = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
249
250           Item * lc = wish->column_l ();
251           Grob * right = Note_spacing::right_column (wish);
252
253           if (!right)
254             continue;
255
256           Item * rc = dynamic_cast<Item*> (right);
257
258           int right_rank = Paper_column::rank_i (rc);
259           int left_rank = Paper_column::rank_i (lc);      
260
261           /*
262             update the left column.
263            */
264           if (right_rank <= min_rank)
265             {
266               if (right_rank < min_rank)
267                 right_neighbors  =SCM_EOL;
268               
269               min_rank = right_rank;
270               right_neighbors = gh_cons (wish->self_scm (), right_neighbors);
271             }
272
273           /*
274             update the right column of the wish.
275            */
276           int maxrank = 0;
277           SCM left_neighs = rc->get_grob_property ("left-neighbors");
278           if (gh_pair_p (left_neighs)
279               && unsmob_grob (gh_car (left_neighs)))
280             {
281               Item * it = dynamic_cast<Item*> (unsmob_grob (gh_car (left_neighs)));
282               maxrank = Paper_column::rank_i (it->column_l());
283             }
284
285           if (left_rank >= maxrank)
286             {
287               if (left_rank > maxrank)
288                 left_neighs = SCM_EOL;
289
290               left_neighs = gh_cons (wish->self_scm (), left_neighs);
291               rc->set_grob_property ("left-neighbors", right_neighbors);
292             }
293         }
294
295       if (gh_pair_p (right_neighbors))
296         {
297           cols[i]->set_grob_property ("right-neighbors", right_neighbors);
298         }
299     }
300 }
301
302 /*
303   Set neighboring columns that have no left/right-neighbor set
304   yet. Only do breakable non-musical columns, and musical columns. 
305 */
306 void
307 Spacing_spanner::set_implicit_neighbor_columns (Link_array<Grob> cols)
308 {
309   for (int i = 0; i < cols.size (); i++)
310     {
311       Item * it = dynamic_cast<Item*>(cols[i]);
312       if (!Item::breakable_b (it) && !Paper_column::musical_b (it))
313         continue;
314
315       // it->breakable || it->musical
316
317       /*
318         sloppy with typnig left/right-neighbors should take list, but paper-column found instead.
319        */
320       SCM ln = cols[i] ->get_grob_property ("left-neighbors");
321       if (!gh_pair_p (ln) && i ) 
322         {
323           cols[i]->set_grob_property ("left-neighbors", gh_cons (cols[i-1]->self_scm(), SCM_EOL));
324         }
325
326       SCM rn = cols[i] ->get_grob_property ("right-neighbors");
327       if (!gh_pair_p (rn) && i < cols.size () - 1) 
328         {
329           cols[i]->set_grob_property ("right-neighbors", gh_cons (cols[i + 1]->self_scm(), SCM_EOL));
330         }
331     }
332 }
333
334
335 MAKE_SCHEME_CALLBACK (Spacing_spanner, set_springs,1);
336 SCM
337 Spacing_spanner::set_springs (SCM smob)
338 {
339   Grob *me = unsmob_grob (smob);
340
341   Link_array<Grob> all (me->pscore_l_->line_l_->column_l_arr ()) ;
342
343   set_explicit_neighbor_columns (all);
344
345   Rational global_shortest = find_shortest (all);
346   prune_loose_colunms (me, &all, global_shortest);
347   set_implicit_neighbor_columns (all);
348
349   
350   int j = 0;
351   for (int i = 1; i < all.size (); i++)
352     {
353       Grob *sc = all[i];
354       if (Item::breakable_b (sc))
355         {
356           Link_array<Grob> measure (all.slice (j, i+1));          
357           do_measure (global_shortest, me, &measure);
358           j = i;
359         }
360     }
361
362   return SCM_UNSPECIFIED;
363 }
364
365
366 /*
367   We want the shortest note that is also "common" in the piece, so we
368   find the shortest in each measure, and take the most frequently
369   found duration.
370
371   This probably gives weird effects with modern music, where every
372   note has a different duration, but hey, don't write that kind of
373   stuff, then.
374
375 */
376 Rational
377 Spacing_spanner::find_shortest (Link_array<Grob> const &cols)
378 {
379   /*
380     ascending in duration
381    */
382   Array<Rational> durations; 
383   Array<int> counts;
384   
385   Rational shortest_in_measure;
386   shortest_in_measure.set_infinite (1);
387   
388   for (int i =0 ; i < cols.size (); i++)  
389     {
390       if (Paper_column::musical_b (cols[i]))
391         {
392           Moment *when = unsmob_moment (cols[i]->get_grob_property  ("when"));
393
394           /*
395             ignore grace notes for shortest notes.
396           */
397           if (when && when->grace_part_)
398             continue;
399           
400           SCM  st = cols[i]->get_grob_property ("shortest-starter-duration");
401           Moment this_shortest = *unsmob_moment (st);
402           assert (this_shortest.to_bool());
403           shortest_in_measure = shortest_in_measure <? this_shortest.main_part_;
404         }
405       else if (!shortest_in_measure.infty_b()
406                && Item::breakable_b (cols[i]))
407         {
408           int j = 0;
409           for (; j < durations.size(); j++)
410             {
411               if (durations[j] > shortest_in_measure)
412                 {
413                   counts.insert (1, j);
414                   durations.insert (shortest_in_measure, j);
415                   break;
416                 }
417               else if (durations[j] == shortest_in_measure)
418                 {
419                   counts[j]++;
420                   break;
421                 }
422             }
423
424           if (durations.size() == j)
425             {
426               durations.push (shortest_in_measure);
427               counts.push (1);
428             }
429
430           shortest_in_measure.set_infinite(1); 
431         }
432     }
433
434   int max_idx = -1;
435   int max_count = 0;
436   for (int i =durations.size(); i--;)
437     {
438       if (counts[i] >= max_count)
439         {
440           max_idx = i;
441           max_count = counts[i];
442         }
443
444       //      printf ("duration %d/%d, count %d\n", durations[i].num (), durations[i].den (), counts[i]);
445     }
446
447   /*
448     TODO: 1/8 should be adjustable?
449    */
450   Rational d = Rational (1,8);
451   if (max_idx >= 0)
452     d = d <? durations[max_idx] ;
453
454   return d;
455 }
456
457 /*
458   Generate spacing for a single measure. We used to have code that did
459   per-measure spacing. Now we have piecewise spacing. We should fix
460   this to support "spacing-regions": some regions have different notes
461   (different time sigs) than others, and should be spaced differently.
462  */
463 void
464 Spacing_spanner::do_measure (Rational shortest, Grob*me, Link_array<Grob> *cols) 
465 {
466
467   Real headwid =       gh_scm2double (me->get_grob_property ("spacing-increment"));
468   for (int i= 0; i < cols->size () - 1; i++)
469     {
470       Item * l = dynamic_cast<Item*> (cols->elem (i));
471       Item * r =  dynamic_cast<Item*> (cols->elem (i+1));
472
473       Paper_column * lc = dynamic_cast<Paper_column*> (l);
474       Paper_column * rc = dynamic_cast<Paper_column*> (r);
475
476       if (!Paper_column::musical_b (l))
477         {
478           breakable_column_spacing (me, l, r, shortest);
479
480           /*
481             
482             The case that the right part is broken as well is rather
483             rare, but it is possible, eg. with a single empty measure,
484             or if one staff finishes a tad earlier than the rest.
485             
486            */
487           Item *lb = l->find_prebroken_piece (RIGHT);
488           Item *rb = r->find_prebroken_piece (LEFT);
489           
490           if (lb)
491             breakable_column_spacing (me, lb,r, shortest);
492
493           if (rb)
494             breakable_column_spacing (me, l, rb, shortest);
495           if (lb && rb)
496             breakable_column_spacing (me, lb, rb, shortest);
497           
498           continue ; 
499         }
500
501
502       musical_column_spacing (me, lc, rc, headwid, shortest);
503       if (Item *rb = r->find_prebroken_piece (LEFT))
504         musical_column_spacing (me, lc, rb, headwid, shortest);
505     }    
506 }
507
508
509 /*
510   Generate the space between two musical columns LC and RC, given spacing parameters INCR and SHRTEST.
511  */
512 void
513 Spacing_spanner::musical_column_spacing (Grob *me, Item * lc, Item *rc, Real increment, Rational shortest)
514 {
515   bool expand_only = false;
516   Real base_note_space = note_spacing (me, lc, rc, shortest, &expand_only);
517
518   Real max_note_space = -infinity_f;
519   Real max_fixed_note_space = -infinity_f;
520
521   SCM seq  = lc->get_grob_property ("right-neighbors");
522
523   /*
524     We adjust the space following a note only if the next note
525     happens after the current note (this is set in the grob
526     property SPACING-SEQUENCE.
527   */
528   for (SCM s = seq; gh_pair_p (s); s = ly_cdr (s))
529     {
530       Grob * wish = unsmob_grob (gh_car (s));
531
532       Item *wish_rcol = Note_spacing::right_column (wish);
533       if (Note_spacing::left_column (wish) != lc
534           || (wish_rcol != rc && wish_rcol != rc->original_l_))
535         continue;
536
537       /*
538         This is probably a waste of time in the case of polyphonic
539         music.  */
540       if (Note_spacing::has_interface (wish))
541         {
542           Real space =0.0;
543           Real fixed =0.0;
544           
545           Note_spacing::get_spacing (wish, rc, base_note_space, increment, &space, &fixed);
546           max_note_space = max_note_space >? space;
547           max_fixed_note_space = max_fixed_note_space >? fixed;
548         }
549     }
550
551   if (max_note_space < 0)
552     {
553       max_note_space = base_note_space;
554       max_fixed_note_space = increment;
555     }
556
557   bool ragged = to_boolean (me->paper_l ()->get_scmvar ("raggedright"));
558   Real strength = (ragged) ? 1.0 : 1 / (max_note_space - max_fixed_note_space);
559   Real distance = (ragged) ? max_fixed_note_space : max_note_space;
560   Spaceable_grob::add_spring (lc, rc, distance, strength, expand_only);
561 }
562
563 void
564 Spacing_spanner::standard_breakable_column_spacing (Grob * me, Item*l, Item*r,
565                                    Real * fixed, Real * space,
566                                    Moment shortest)
567 {
568   *fixed = l->extent (l, X_AXIS)[RIGHT] - r->extent (r, X_AXIS)[LEFT];
569       
570   if (l->breakable_b (l) && r->breakable_b(r))
571     {
572       Moment *dt = unsmob_moment (l->get_grob_property ("measure-length"));
573       Moment mlen (1);
574       if (dt)
575         mlen = *dt;
576       
577       Real incr = gh_scm2double (me->get_grob_property ("spacing-increment"));
578
579       *space =  *fixed + incr * double (mlen.main_part_ / shortest.main_part_) * 0.8;
580     }
581   else
582     {
583       Moment dt = Paper_column::when_mom (r) - Paper_column::when_mom (l);
584       bool dummy;
585
586       *space = *fixed + get_duration_space (me, dt, shortest.main_part_, &dummy);
587     }
588   
589   
590 }
591
592
593 /*
594   Read hints from L and generate springs.
595  */
596 void
597 Spacing_spanner::breakable_column_spacing (Grob*me, Item* l, Item *r,Moment shortest)
598 {
599   Real max_fixed = -infinity_f;
600   Real max_space = -infinity_f;
601
602   standard_breakable_column_spacing (me, l, r, &max_fixed, &max_space ,
603                                      shortest);
604   
605   for (SCM s = l->get_grob_property ("spacing-wishes");
606        gh_pair_p (s); s = gh_cdr (s))
607     {
608       Item * spacing_grob = dynamic_cast<Item*> (unsmob_grob (gh_car (s)));
609
610       if (!spacing_grob || !Staff_spacing::has_interface (spacing_grob))
611         continue;
612
613       Real space;
614       Real fixed_space;
615
616       /*
617         column for the left one settings should be ok due automatic
618         pointer munging.
619
620       */
621       assert (spacing_grob-> column_l () == l);
622
623       Staff_spacing::get_spacing_params (spacing_grob,
624                                          &space, &fixed_space);  
625       if (space > max_space)
626         {
627           max_space = space;
628           max_fixed = fixed_space;
629         }
630     }
631
632   
633   
634
635   if (isinf (max_space))
636     {
637       programming_error ("No pref spacing found");
638       max_space = 2.0;
639       max_fixed = 1.0;
640     }
641
642   
643   if (l->break_status_dir() == RIGHT
644       && Paper_column::when_mom (l) == Paper_column::when_mom (r))
645     {
646       /* Start of line: this space is not stretchable */
647       max_fixed = max_space;
648     }
649
650   /*
651     Hmm.  we do 1/0 in the next thing. Perhaps we should check if this
652     works on all architectures.
653    */
654   
655   bool ragged = to_boolean (me->paper_l ()->get_scmvar ("raggedright"));
656   Real strength = (ragged) ? 1.0 : 1 / (max_space - max_fixed);
657   Real distance = (ragged) ? max_fixed : max_space;
658   Spaceable_grob::add_spring (l, r, distance, strength, false);
659 }
660
661
662 /**
663   Get the measure wide ant for arithmetic spacing.
664   */
665 Real
666 Spacing_spanner::get_duration_space (Grob*me, Moment d, Rational shortest, bool * expand_only) 
667 {
668   Real k = gh_scm2double (me->get_grob_property ("shortest-duration-space"));
669   Real incr = gh_scm2double (me->get_grob_property ("spacing-increment"));
670   
671   if (d < shortest)
672     {
673       /*
674         We don't space really short notes using the log of the
675         duration, since it would disproportionally stretches the long
676         notes in a piece. In stead, we use geometric spacing with constant 0.5
677         (i.e. linear.)
678
679         This should probably be tunable, to use other base numbers.
680
681         In Mozart hrn3 by EB., we have 8th note = 3.9 mm (total), 16th note =
682         3.6 mm (total).  head-width = 2.4, so we 1.2mm for 16th, 1.5
683         mm for 8th. (white space), suggesting that we use
684
685         (1.2 / 1.5)^{-log2(duration ratio)}
686         
687
688        */
689       Rational ratio = d.main_part_ / shortest;
690       
691       *expand_only = true;
692       return ((k-1) + double (ratio)) * incr;
693     }
694   else
695     {
696       /*
697           John S. Gourlay. ``Spacing a Line of Music,'' Technical
698           Report OSU-CISRC-10/87-TR35, Department of Computer and
699           Information Science, The Ohio State University, 1987.
700        */
701       Real log =  log_2 (shortest);
702       k -= log;
703       Rational compdur = d.main_part_ + d.grace_part_ /Rational (3);
704       *expand_only = false;      
705    
706       return (log_2 (compdur) + k) * incr;
707     }
708 }
709
710 Real
711 Spacing_spanner::note_spacing (Grob*me, Grob *lc, Grob *rc,
712                                Moment shortest, bool * expand_only) 
713 {
714   Moment shortest_playing_len = 0;
715   SCM s = lc->get_grob_property ("shortest-playing-duration");
716
717   if (unsmob_moment (s))
718     shortest_playing_len = *unsmob_moment (s);
719   
720   if (! shortest_playing_len.to_bool ())
721     {
722       programming_error ("can't find a ruling note at " + Paper_column::when_mom (lc).str ());
723       shortest_playing_len = 1;
724     }
725
726   Moment lwhen = Paper_column::when_mom (lc);
727   Moment rwhen =  Paper_column::when_mom (rc);
728
729   Moment delta_t = rwhen - lwhen;
730   Real dist = 0.0;
731
732   if (delta_t.main_part_ && !lwhen.grace_part_)
733     {
734       dist = get_duration_space (me, shortest_playing_len, shortest.main_part_, expand_only);
735       dist *= (double) (delta_t.main_part_ / shortest_playing_len.main_part_);
736     }
737   else if (delta_t.grace_part_)
738     {
739       /*
740         TODO: figure out how to space grace notes.
741       */
742       dist = get_duration_space (me, shortest, shortest.main_part_, expand_only);
743
744       Real grace_fact = 1.0;
745       SCM gf = me->get_grob_property ("grace-space-factor");
746       if (gh_number_p (gf))
747         grace_fact = gh_scm2double (gf);
748
749       dist *= grace_fact;
750     }
751
752   
753   return dist;
754 }
755
756
757
758 ADD_INTERFACE (Spacing_spanner,"spacing-spanner-interface",
759   "
760 The space taken by a note is dependent on its duration. Doubling a
761 duration adds spacing-increment to the space. The most common shortest
762 note gets shortest-duration-space. Notes that are even shorter are
763 spaced proportonial to their duration.
764
765 Typically, the increment is the width of a black note head.  In a
766 piece with lots of 8th notes, and some 16th notes, the eighth note
767 gets 2 note heads width (i.e. the space following a note is 1 note
768 head width) A 16th note is followed by 0.5 note head width. The
769 quarter note is followed by  3 NHW, the half by 4 NHW, etc.
770 ",
771   "grace-space-factor spacing-increment shortest-duration-space");
772