+Real
+Stem_tremolo::y_offset (Grob *me, bool pure)
+{
+ Item *stem = unsmob_item (me->get_object ("stem"));
+ if (!stem)
+ return 0.0;
+
+ Direction stemdir = get_grob_direction (stem);
+ if (stemdir == 0)
+ stemdir = UP;
+
+ Spanner *beam = Stem::get_beam (stem);
+ Real beam_translation = get_beam_translation (me);
+
+ int beam_count = beam ? (Stem::beam_multiplicity (stem).length () + 1) : 0;
+
+ if (pure && beam)
+ {
+ Interval ph = stem->pure_height (stem, 0, INT_MAX);
+ Stem_info si = Stem::get_stem_info (stem);
+ ph[-stemdir] = si.shortest_y_;
+
+ return (ph - stemdir * max (beam_count, 1) * beam_translation)[stemdir] - stemdir * 0.5 * me->pure_height (me, 0, INT_MAX).length ();
+ }
+
+ Real end_y
+ = (pure
+ ? stem->pure_height (stem, 0, INT_MAX)[stemdir]
+ : stem->extent (stem, Y_AXIS)[stemdir])
+ - stemdir * max (beam_count, 1) * beam_translation
+ - Stem::beam_end_corrective (stem);
+
+ if (!beam && Stem::duration_log (stem) >= 3)
+ {
+ end_y -= stemdir * (Stem::duration_log (stem) - 2) * beam_translation;
+ if (stemdir == UP)
+ end_y -= stemdir * beam_translation * 0.5;
+ }
+
+ bool whole_note = Stem::duration_log (stem) <= 0;
+ if (whole_note)
+ {
+ /* we shouldn't position relative to the end of the stem since the stem
+ is invisible */
+ Real ss = Staff_symbol_referencer::staff_space (me);
+ vector<int> nhp = Stem::note_head_positions (stem);
+ Real note_head = (stemdir == UP ? nhp.back () : nhp[0]) * ss / 2;
+ end_y = note_head + stemdir * 1.5;
+ }
+
+ return end_y;
+}
+
+MAKE_SCHEME_CALLBACK (Stem_tremolo, print, 1);
+SCM
+Stem_tremolo::print (SCM grob)
+{
+ Grob *me = unsmob_grob (grob);
+
+ Stencil s = untranslated_stencil (me, robust_scm2double (me->get_property ("slope"), 0.25));
+ return s.smobbed_copy ();
+}
+
+ADD_INTERFACE (Stem_tremolo,
+ "A beam slashing a stem to indicate a tremolo. The property"
+ " @code{style} can be @code{default} or @code{rectangle}.",
+
+ /* properties */
+ "beam-thickness "
+ "beam-width "
+ "flag-count "
+ "length-fraction "
+ "stem "
+ "style "
+ "slope "
+ );