*** empty log message ***
[lilypond.git] / lily / stem.cc
blobf0f30ea6855f0358ff4cd1cca85a5c3e8b8c039b
1 /*
2 stem.cc -- implement Stem
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
9 TODO: This is way too hairy
11 TODO: fix naming.
13 Stem-end, chord-start, etc. is all confusing naming.
16 #include <math.h> // rint
18 #include "lookup.hh"
19 #include "directional-element-interface.hh"
20 #include "note-head.hh"
21 #include "stem.hh"
22 #include "warn.hh"
23 #include "paper-def.hh"
24 #include "rhythmic-head.hh"
25 #include "font-interface.hh"
26 #include "molecule.hh"
27 #include "paper-column.hh"
28 #include "misc.hh"
29 #include "beam.hh"
30 #include "rest.hh"
31 #include "group-interface.hh"
32 #include "staff-symbol-referencer.hh"
33 #include "spanner.hh"
34 #include "side-position-interface.hh"
35 #include "dot-column.hh"
37 void
38 Stem::set_beaming (Grob*me, int beam_count, Direction d)
40 SCM pair = me->get_grob_property ("beaming");
42 if (!gh_pair_p (pair))
44 pair = gh_cons (SCM_EOL, SCM_EOL);
45 me->set_grob_property ("beaming", pair);
48 SCM l = index_get_cell (pair, d);
49 for( int i = 0; i< beam_count; i++)
51 l = gh_cons (gh_int2scm (i), l);
53 index_set_cell (pair, d, l);
57 Interval
58 Stem::head_positions (Grob*me)
60 if (!head_count (me))
62 Interval iv;
63 return iv;
66 Drul_array<Grob*> e (extremal_heads (me));
68 return Interval (Staff_symbol_referencer::get_position (e[DOWN]),
69 Staff_symbol_referencer::get_position (e[UP]));
73 Real
74 Stem::chord_start_y (Grob*me)
76 return head_positions (me)[get_direction (me)]
77 * Staff_symbol_referencer::staff_space (me)/2.0;
80 Real
81 Stem::stem_end_position (Grob*me)
83 SCM p =me->get_grob_property ("stem-end-position");
84 Real pos;
85 if (!gh_number_p (p))
87 pos = get_default_stem_end_position (me);
88 me->set_grob_property ("stem-end-position", gh_double2scm (pos));
90 else
91 pos = gh_scm2double (p);
93 return pos;
96 Direction
97 Stem::get_direction (Grob*me)
99 Direction d = Directional_element_interface::get (me);
101 if (!d)
103 d = get_default_dir (me);
104 // urg, AAARGH!
105 Directional_element_interface::set (me, d);
107 return d ;
111 void
112 Stem::set_stemend (Grob*me, Real se)
114 // todo: margins
115 Direction d= get_direction (me);
117 if (d && d * head_positions (me)[get_direction (me)] >= se*d)
118 me->warning (_ ("Weird stem size; check for narrow beams"));
120 me->set_grob_property ("stem-end-position", gh_double2scm (se));
125 Note head that determines hshift for upstems
127 WARNING: triggers direction
129 Grob*
130 Stem::support_head (Grob*me)
132 SCM h = me->get_grob_property ("support-head");
133 Grob * nh = unsmob_grob (h);
134 if (nh)
135 return nh;
136 else if (head_count (me) == 1)
139 UGH.
142 return unsmob_grob (ly_car (me->get_grob_property ("note-heads")));
144 else
145 return first_head (me);
150 Stem::head_count (Grob*me)
152 return Pointer_group_interface::count (me, "note-heads");
156 The note head which forms one end of the stem.
158 WARNING: triggers direction
160 Grob*
161 Stem::first_head (Grob*me)
163 Direction d = get_direction (me);
164 if (!d)
165 return 0;
166 return extremal_heads (me)[-d];
170 The note head opposite to the first head.
172 Grob*
173 Stem::last_head (Grob*me)
175 Direction d = get_direction (me);
176 if (!d)
177 return 0;
178 return extremal_heads (me)[d];
182 START is part where stem reaches `last' head.
184 Drul_array<Grob*>
185 Stem::extremal_heads (Grob*me)
187 const int inf = 1000000;
188 Drul_array<int> extpos;
189 extpos[DOWN] = inf;
190 extpos[UP] = -inf;
192 Drul_array<Grob *> exthead;
193 exthead[LEFT] = exthead[RIGHT] =0;
195 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
197 Grob * n = unsmob_grob (ly_car (s));
200 int p = int (Staff_symbol_referencer::get_position (n));
202 Direction d = LEFT;
203 do {
204 if (d* p > d* extpos[d])
206 exthead[d] = n;
207 extpos[d] = p;
209 } while (flip (&d) != DOWN);
212 return exthead;
215 static int
216 icmp (int const &a, int const &b)
218 return a-b;
221 Array<int>
222 Stem::note_head_positions (Grob *me)
224 Array<int> ps ;
225 for (SCM s = me->get_grob_property ("note-heads"); gh_pair_p (s); s = ly_cdr (s))
227 Grob * n = unsmob_grob (ly_car (s));
228 int p = int (Staff_symbol_referencer::get_position (n));
230 ps.push (p);
233 ps.sort (icmp);
234 return ps;
238 void
239 Stem::add_head (Grob*me, Grob *n)
241 n->set_grob_property ("stem", me->self_scm ());
242 n->add_dependency (me);
244 if (Note_head::has_interface (n))
246 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-heads"), n);
250 bool
251 Stem::invisible_b (Grob*me)
253 return ! (head_count (me)
254 && gh_scm2int (me->get_grob_property ("duration-log")) >= 1);
257 Direction
258 Stem::get_default_dir (Grob*me)
260 int staff_center = 0;
261 Interval hp = head_positions (me);
262 if (hp.empty_b())
264 return CENTER;
267 int udistance = (int) (UP * hp[UP] - staff_center);
268 int ddistance = (int) (DOWN* hp[DOWN] - staff_center);
270 if (sign (ddistance - udistance))
271 return Direction (sign (ddistance -udistance));
273 return to_dir (me->get_grob_property ("neutral-direction"));
276 Real
277 Stem::get_default_stem_end_position (Grob*me)
279 /* Tab notation feature: make stem end extend out of staff. */
280 SCM up_to_staff = me->get_grob_property ("up-to-staff");
281 if (to_boolean (up_to_staff))
283 int line_count = Staff_symbol_referencer::line_count (me);
284 Direction dir = get_direction (me);
285 return dir * (line_count + 3.5);
288 bool grace_b = to_boolean (me->get_grob_property ("grace"));
289 SCM s;
290 Array<Real> a;
292 Real length = 3.5;
293 SCM scm_len = me->get_grob_property ("length");
294 if (gh_number_p (scm_len))
296 length = gh_scm2double (scm_len);
298 else
300 s = me->get_grob_property ("lengths");
301 if (gh_pair_p (s))
303 length = 2* gh_scm2double (robust_list_ref (duration_log(me) -2, s));
307 Real shorten = 0.0;
309 SCM sshorten = me->get_grob_property ("stem-shorten");
310 SCM scm_shorten = gh_pair_p (sshorten) ?
311 robust_list_ref ((duration_log (me) - 2) >? 0, sshorten): SCM_EOL;
312 if (gh_number_p (scm_shorten))
314 shorten = 2* gh_scm2double (scm_shorten);
319 /* URGURGURG
320 'set-default-stemlen' sets direction too
322 Direction dir = get_direction (me);
323 if (!dir)
325 dir = get_default_dir (me);
326 Directional_element_interface::set (me, dir);
329 /* On boundary: shorten only half */
330 if (abs (head_positions (me)[get_direction (me)]) <= 1)
331 shorten *= 0.5;
333 /* stems in unnatural (forced) direction should be shortened,
334 according to [Roush & Gourlay] */
335 if (!chord_start_y (me)
336 || (get_direction (me) != get_default_dir (me)))
337 length -= shorten;
339 Interval hp = head_positions (me);
340 Real st = hp[dir] + dir * length;
343 TODO: change name to extend-stems to staff/center/'()
345 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
346 if (!grace_b && !no_extend_b && dir * st < 0) // junkme?
347 st = 0.0;
350 Make a little room if we have a upflag and there is a dot.
351 previous approach was to lengthen the stem. This is not
352 good typesetting practice.
355 if (!get_beam (me) && dir == UP
356 && duration_log (me) > 2)
358 Grob * closest_to_flag = extremal_heads (me)[dir];
359 Grob * dots = closest_to_flag
360 ? Rhythmic_head::get_dots (closest_to_flag ) : 0;
362 if (dots)
364 Real dp = Staff_symbol_referencer::get_position (dots);
365 Real flagy = flag (me).extent (Y_AXIS)[-dir] * 2
366 / Staff_symbol_referencer::staff_space (me);
369 Very gory: add myself to the X-support of the parent,
370 which should be a dot-column.
372 if (dir * (st + flagy - dp) < 0.5)
374 Grob *par = dots->get_parent (X_AXIS);
376 if (Dot_column::has_interface (par))
378 Side_position_interface::add_support (par, me);
381 TODO: apply some better logic here. The flag is
382 curved inwards, so this will typically be too
383 much.
391 return st;
398 the log of the duration (Number of hooks on the flag minus two)
401 Stem::duration_log (Grob*me)
403 SCM s = me->get_grob_property ("duration-log");
404 return (gh_number_p (s)) ? gh_scm2int (s) : 2;
407 void
408 Stem::position_noteheads (Grob*me)
410 if (!head_count (me))
411 return;
413 Link_array<Grob> heads =
414 Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-heads");
416 heads.sort (compare_position);
417 Direction dir =get_direction (me);
419 if (dir < 0)
420 heads.reverse ();
423 Real thick = gh_scm2double (me->get_grob_property ("thickness"))
424 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
426 Grob *hed = support_head (me);
427 Real w = Note_head::head_extent (hed,X_AXIS)[dir];
428 for (int i=0; i < heads.size (); i++)
430 heads[i]->translate_axis (w - Note_head::head_extent (heads[i],X_AXIS)[dir],
431 X_AXIS);
434 bool parity= true;
435 int lastpos = int (Staff_symbol_referencer::get_position (heads[0]));
436 for (int i=1; i < heads.size (); i ++)
438 Real p = Staff_symbol_referencer::get_position (heads[i]);
439 int dy =abs (lastpos- (int)p);
441 if (dy <= 1)
443 if (parity)
445 Real l = Note_head::head_extent (heads[i], X_AXIS).length ();
447 Direction d = get_direction (me);
448 /* reversed head should be shifted l-thickness, but this looks
449 too crowded, so we only shift l-0.5*thickness.
450 Notice that this leads to assymetry: Normal heads overlap
451 the stem 100% whereas reversed heads only overlaps the stem
452 50% */
453 #define magic 0.5
454 heads[i]->translate_axis ((l-thick*magic) * d, X_AXIS);
456 if (invisible_b(me))
457 heads[i]->translate_axis (-thick*(2-magic) * d , X_AXIS);
460 /* TODO:
462 For some cases we should kern some more: when the
463 distance between the next or prev note is too large, we'd
464 get large white gaps, eg.
468 |X <- kern this.
474 parity = !parity;
476 else
477 parity = true;
479 lastpos = int (p);
483 MAKE_SCHEME_CALLBACK (Stem,before_line_breaking,1);
485 Stem::before_line_breaking (SCM smob)
487 Grob*me = unsmob_grob (smob);
491 Do the calculations for visible stems, but also for invisible stems
492 with note heads (i.e. half notes.)
494 if (head_count (me))
496 stem_end_position (me); // ugh. Trigger direction calc.
497 position_noteheads (me);
499 else
501 me->set_grob_property ("molecule-callback", SCM_EOL);
504 return SCM_UNSPECIFIED;
508 ugh.
509 When in a beam with tuplet brackets, brew_mol is called early,
510 caching a wrong value.
512 MAKE_SCHEME_CALLBACK (Stem, height, 2);
514 Stem::height (SCM smob, SCM ax)
516 Axis a = (Axis)gh_scm2int (ax);
517 Grob * me = unsmob_grob (smob);
518 assert (a == Y_AXIS);
520 SCM mol = me->get_uncached_molecule ();
521 Interval iv;
522 if (mol != SCM_EOL)
523 iv = unsmob_molecule (mol)->extent (a);
524 if (Grob *b =get_beam (me))
526 Direction d = get_direction (me);
527 iv[d] += d * Beam::get_thickness (b) /2.0 ;
530 return ly_interval2scm (iv);
534 Molecule
535 Stem::flag (Grob*me)
537 /* TODO: maybe property stroke-style should take different values,
538 e.g. "" (i.e. no stroke), "single" and "double" (currently, it's
539 '() or "grace"). */
540 String flag_style;
542 SCM flag_style_scm = me->get_grob_property ("flag-style");
543 if (gh_symbol_p (flag_style_scm))
545 flag_style = ly_symbol2string (flag_style_scm);
548 if (flag_style == "no-flag")
550 return Molecule ();
553 bool adjust = to_boolean (me->get_grob_property ("adjust-if-on-staffline"));
555 String staffline_offs;
556 if (String::compare (flag_style, "mensural") == 0)
557 /* Mensural notation: For notes on staff lines, use different
558 flags than for notes between staff lines. The idea is that
559 flags are always vertically aligned with the staff lines,
560 regardless if the note head is on a staff line or between two
561 staff lines. In other words, the inner end of a flag always
562 touches a staff line.
565 if (adjust)
567 /* Urrgh! We have to detect wether this stem ends on a staff
568 line or between two staff lines. But we can not call
569 stem_end_position(me) or get_default_stem_end_position(me),
570 since this encounters the flag and hence results in an
571 infinite recursion. However, in pure mensural notation,
572 there are no multiple note heads attached to a single stem,
573 neither is there usually need for using the stem_shorten
574 property (except for 32th and 64th notes, but that is not a
575 problem since the stem length in this case is augmented by
576 an integral multiple of staff_space). Hence, it should be
577 sufficient to just take the first note head, assume it's
578 the only one, look if it's on a staff line, and select the
579 flag's shape accordingly. In the worst case, the shape
580 looks slightly misplaced, but that will usually be the
581 programmer's fault (e.g. when trying to attach multiple
582 note heads to a single stem in mensural notation). */
585 perhaps the detection whether this correction is needed should
586 happen in a different place to avoid the recursion.
588 --hwn.
590 int p = (int)rint (Staff_symbol_referencer::get_position (first_head (me)));
591 staffline_offs = Staff_symbol_referencer::on_staffline (me, p) ?
592 "1" : "0";
594 else
596 staffline_offs = "2";
599 else
601 staffline_offs = "";
604 char dir = (get_direction (me) == UP) ? 'u' : 'd';
605 String font_char =
606 flag_style + to_string (dir) + staffline_offs + to_string (duration_log (me));
607 Font_metric *fm = Font_interface::get_default_font (me);
608 Molecule flag = fm->find_by_name ("flags-" + font_char);
609 if (flag.empty_b ())
611 me->warning (_f ("flag `%s' not found", font_char));
614 SCM stroke_style_scm = me->get_grob_property ("stroke-style");
615 if (gh_string_p (stroke_style_scm))
617 String stroke_style = ly_scm2string (stroke_style_scm);
618 if (!stroke_style.empty_b ())
620 String font_char = to_string (dir) + stroke_style;
621 Molecule stroke = fm->find_by_name ("flags-" + font_char);
622 if (stroke.empty_b ())
624 me->warning (_f ("flag stroke `%s' not found", font_char));
626 else
628 flag.add_molecule (stroke);
633 return flag;
636 MAKE_SCHEME_CALLBACK (Stem,dim_callback,2);
638 Stem::dim_callback (SCM e, SCM ax)
640 Axis a = (Axis) gh_scm2int (ax);
641 assert (a == X_AXIS);
642 Grob *se = unsmob_grob (e);
643 Interval r (0, 0);
644 if (unsmob_grob (se->get_grob_property ("beam")) || abs (duration_log (se)) <= 2)
645 ; // TODO!
646 else
648 r = flag (se).extent (X_AXIS);
650 return ly_interval2scm (r);
655 MAKE_SCHEME_CALLBACK (Stem,brew_molecule,1);
658 Stem::brew_molecule (SCM smob)
660 Grob*me = unsmob_grob (smob);
661 Molecule mol;
662 Direction d = get_direction (me);
666 Real y1;
669 This is required to avoid stems passing in tablature chords...
674 TODO: make the stem start a direction ?
676 if (to_boolean (me->get_grob_property ("avoid-note-head")))
678 Grob * lh = last_head (me);
679 if (!lh)
680 return SCM_EOL;
681 y1 = Staff_symbol_referencer::get_position (lh);
683 else
685 Grob * lh = first_head (me);
686 if (!lh)
687 return SCM_EOL;
688 y1 = Staff_symbol_referencer::get_position (lh);
691 Real y2 = stem_end_position (me);
693 Interval stem_y (y1 <? y2,y2 >? y1);
696 // dy?
697 Real dy = Staff_symbol_referencer::staff_space (me) * 0.5;
699 if (Grob *hed = support_head (me))
702 must not take ledgers into account.
704 Interval head_height = Note_head::head_extent (hed,Y_AXIS);
705 Real y_attach = Note_head::stem_attachment_coordinate ( hed, Y_AXIS);
707 y_attach = head_height.linear_combination (y_attach);
708 stem_y[Direction (-d)] += d * y_attach/dy;
711 if (!invisible_b (me))
713 Real stem_width = gh_scm2double (me->get_grob_property ("thickness"))
714 // URG
715 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
717 Molecule ss =Lookup::filledbox (Box (Interval (-stem_width/2, stem_width/2),
718 Interval (stem_y[DOWN]*dy, stem_y[UP]*dy)));
719 mol.add_molecule (ss);
722 if (!get_beam (me) && abs (duration_log (me)) > 2)
724 Molecule fl = flag (me);
725 fl.translate_axis (stem_y[d]*dy, Y_AXIS);
726 mol.add_molecule (fl);
729 return mol.smobbed_copy ();
733 move the stem to right of the notehead if it is up.
735 MAKE_SCHEME_CALLBACK (Stem,off_callback,2);
737 Stem::off_callback (SCM element_smob, SCM)
739 Grob *me = unsmob_grob (element_smob);
741 Real r=0;
743 if (head_count (me) == 0)
745 return gh_double2scm (0.0);
748 if (Grob * f = first_head (me))
750 Interval head_wid = Note_head::head_extent(f, X_AXIS);
753 Real attach =0.0;
755 if (invisible_b (me))
757 attach = 0.0;
759 else
760 attach = Note_head::stem_attachment_coordinate(f, X_AXIS);
762 Direction d = get_direction (me);
764 Real real_attach = head_wid.linear_combination (d * attach);
766 r = real_attach;
769 If not centered: correct for stem thickness.
771 if (attach)
773 Real rule_thick
774 = gh_scm2double (me->get_grob_property ("thickness"))
775 * me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
778 r += - d * rule_thick * 0.5;
781 return gh_double2scm (r);
784 Grob*
785 Stem::get_beam (Grob*me)
787 SCM b= me->get_grob_property ("beam");
788 return unsmob_grob (b);
791 Stem_info
792 Stem::get_stem_info (Grob *me)
794 /* Return cached info if available */
795 SCM scm_info = me->get_grob_property ("stem-info");
796 if (!gh_pair_p (scm_info))
798 calc_stem_info (me);
799 scm_info = me->get_grob_property ("stem-info");
802 Stem_info si;
803 si.dir_ = Directional_element_interface::get (me);
804 si.ideal_y_ = gh_scm2double (gh_car (scm_info));
805 si.shortest_y_ = gh_scm2double (gh_cadr (scm_info));
806 return si;
809 void
810 Stem::calc_stem_info (Grob *me)
812 /* Tab notation feature: make stem end extend out of staff. */
813 SCM up_to_staff = me->get_grob_property ("up-to-staff");
814 if (to_boolean (up_to_staff))
816 int line_count = Staff_symbol_referencer::line_count (me);
817 Direction dir = get_direction (me);
818 Real ideal_y = dir * (line_count + 1.5);
819 Real shortest_y = ideal_y;
821 me->set_grob_property ("stem-info",
822 scm_list_n (gh_double2scm (ideal_y),
823 gh_double2scm (shortest_y),
824 SCM_UNDEFINED));
825 return;
828 Direction my_dir = Directional_element_interface::get (me);
829 Real staff_space = Staff_symbol_referencer::staff_space (me);
830 Grob *beam = get_beam (me);
831 Real beam_translation = Beam::get_beam_translation (beam);
832 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
833 int beam_count = Beam::get_direction_beam_count (beam, my_dir);
836 /* Simple standard stem length */
837 SCM lengths = me->get_grob_property ("beamed-lengths");
838 Real ideal_length =
839 gh_scm2double (robust_list_ref (beam_count - 1,lengths))
841 * staff_space
842 /* stem only extends to center of beam */
843 - 0.5 * beam_thickness;
845 /* Condition: sane minimum free stem length (chord to beams) */
846 lengths = me->get_grob_property ("beamed-minimum-free-lengths");
847 Real ideal_minimum_free =
848 gh_scm2double (robust_list_ref (beam_count - 1, lengths))
849 * staff_space;
852 /* UGH
853 It seems that also for ideal minimum length, we must use
854 the maximum beam count (for this direction):
856 \score{ \notes\relative c''{ [a8 a32] }}
858 must be horizontal. */
859 Real height_of_my_beams = beam_thickness
860 + (beam_count - 1) * beam_translation;
862 Real ideal_minimum_length = ideal_minimum_free
863 + height_of_my_beams
864 /* stem only extends to center of beam */
865 - 0.5 * beam_thickness;
867 ideal_length = ideal_length >? ideal_minimum_length;
870 /* Convert to Y position, calculate for dir == UP */
871 Real note_start =
872 /* staff positions */
873 head_positions (me)[my_dir] * 0.5
874 * my_dir;
875 Real ideal_y = note_start + ideal_length;
878 /* Conditions for Y position */
880 /* Lowest beam of (UP) beam must never be lower than second staffline
882 Reference?
884 Although this (additional) rule is probably correct,
885 I expect that highest beam (UP) should also never be lower
886 than middle staffline, just as normal stems.
888 Reference?
890 Obviously not for grace beams.
892 Also, not for knees. Seems to be a good thing. */
893 SCM grace = me->get_grob_property ("grace");
894 bool grace_b = to_boolean (grace);
895 bool no_extend_b = to_boolean (me->get_grob_property ("no-stem-extend"));
896 bool knee_b = to_boolean (beam->get_grob_property ("knee"));
897 if (!grace_b && !no_extend_b && !knee_b)
899 /* Highest beam of (UP) beam must never be lower than middle
900 staffline */
901 ideal_y = ideal_y >? 0;
902 /* Lowest beam of (UP) beam must never be lower than second staffline */
903 ideal_y = ideal_y >? (-staff_space
904 - beam_thickness + height_of_my_beams);
908 SCM shorten = beam->get_grob_property ("shorten");
909 if (gh_number_p (shorten))
910 ideal_y -= gh_scm2double (shorten);
912 Real minimum_free =
913 gh_scm2double (robust_list_ref
914 (beam_count - 1,
915 me->get_grob_property
916 ("beamed-extreme-minimum-free-lengths")))
917 * staff_space;
919 Real minimum_length = minimum_free
920 + height_of_my_beams
921 /* stem only extends to center of beam */
922 - 0.5 * beam_thickness;
924 Real minimum_y = note_start + minimum_length;
927 ideal_y *= my_dir;
928 Real shortest_y = minimum_y * my_dir;
930 me->set_grob_property ("stem-info",
931 scm_list_n (gh_double2scm (ideal_y),
932 gh_double2scm (shortest_y),
933 SCM_UNDEFINED));
936 Slice
937 Stem::beam_multiplicity (Grob *stem)
939 SCM beaming= stem->get_grob_property ("beaming");
940 Slice l = int_list_to_slice (gh_car (beaming));
941 Slice r = int_list_to_slice (gh_cdr (beaming));
942 l.unite (r);
944 return l;
948 ADD_INTERFACE (Stem,"stem-interface",
949 "A stem",
950 "french-beaming up-to-staff avoid-note-head adjust-if-on-staffline thickness stem-info beamed-lengths beamed-minimum-free-lengths beamed-extreme-minimum-free-lengths lengths beam stem-shorten duration-log beaming neutral-direction stem-end-position support-head note-heads direction length flag-style no-stem-extend stroke-style");