]> git.donarmstrong.com Git - lilypond.git/commitdiff
patch::: 1.5.38.jcn2
authorJan Nieuwenhuizen <janneke@gnu.org>
Tue, 12 Mar 2002 20:20:33 +0000 (21:20 +0100)
committerJan Nieuwenhuizen <janneke@gnu.org>
Tue, 12 Mar 2002 20:20:33 +0000 (21:20 +0100)
2002-03-12  Jan Nieuwenhuizen  <janneke@gnu.org>

* Documentation/topdocs/INSTALL.texi: Add section for MacOS X.

* darwin.patch: New file.

* lily/beam.cc (set_stem_shorten): Revive deceased stem shorten
code.  Shorten stems by fraction of stems to be shortened.

* lily/stem.cc (get_default_stem_end_position): Shorten only half
of shorten value for boundary cases.

* scm/grob-description.scm (Stem): Set stem-shorten to (1.0 0.5).
(Beam): Set beamed-stem-shorten to (1.0 0.5).

---
Generated by janneke@gnu.org,
From = lilypond-1.5.38.jcn1, To = lilypond-1.5.38.jcn2

usage

    cd lilypond-source-dir; patch -E -p1 < lilypond-1.5.38.jcn2.diff

Patches do not contain automatically generated files
or (urg) empty directories,
i.e., you should rerun autoconf, configure

ChangeLog
Documentation/topdocs/INSTALL.texi
GNUmakefile.in
VERSION
darwin.patch [new file with mode: 0644]
input/baerenreiter-sarabande.ly
lily/beam.cc
lily/stem.cc
scm/grob-description.scm

index b42a01f1543e300b104f803573bbdf25bda36497..8d867f846f05f017bdfe4f42242546301f29b6d1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,27 @@
---- ../lilypond-1.5.38.chj1/ChangeLog  Tue Mar 12 01:43:11 2002
+--- ../lilypond-1.5.38.jcn1/ChangeLog  Mon Mar 11 19:49:41 2002
+++ b/ChangeLog Tue Mar 12 21:20:33 2002
+@@ -1,4 +1,21 @@
+2002-03-12  Jan Nieuwenhuizen  <janneke@gnu.org>
+
+       * Documentation/topdocs/INSTALL.texi: Add section for MacOS X.
+
+       * darwin.patch: New file.
+
+       * lily/beam.cc (set_stem_shorten): Revive deceased stem shorten
+       code.  Shorten stems by fraction of stems to be shortened.
+
+       * lily/stem.cc (get_default_stem_end_position): Shorten only half
+       of shorten value for boundary cases.
+
+       * scm/grob-description.scm (Stem): Set stem-shorten to (1.0 0.5).
+       (Beam): Set beamed-stem-shorten to (1.0 0.5).
+
+ 2002-03-11  Jan Nieuwenhuizen  <janneke@gnu.org>
+
+       * lily/beam.cc (check_stem_length_f): Try to lenthen more.
+       * scm/grob-description.scm (Beam): Add concaveness.  Replace
+       Beam::cancel_suspect_slope with Beam::check_concave.--- ../lilypond-1.5.38.chj1/ChangeLog       Tue Mar 12 01:43:11 2002
 ++ b/ChangeLog Tue Mar 12 01:40:48 2002
 @@ -1,3 +1,8 @@
 2002-03-12 Rune Zedeler <rune@zedeler.dk>
index ea6796198d101998e6981f320d30f3acc25c4d19..c99e9b34d1856ac1871da49877eb4208b347f031 100644 (file)
@@ -518,6 +518,48 @@ interfere with your build, you may want to do this before the build too:
        dpkg --purge lilypond lilypond1.3
 @end example
 
+@subsection MacOS X
+
+LilyPond has been built on Darwin, to be precise, on:
+@example
+    Darwin buoux.aspiratie.nl 5.3 Darwin Kernel Version 5.3: Thu Jan 24
+    22:06:02 PST 2002; root:xnu/xnu-201.19.obj~1/RELEASE_PPC  Power Macintosh powerpc
+@end example
+
+using:   
+
+@example
+    Apple Computer, Inc. version gcc-932.1, based on gcc version 2.95.2 19991024 (release)
+@end example
+
+To make sure you have all packages needed to build LilyPond installed,
+run as root:
+
+@example
+        apt-get install bash python guile debianutils flex bison texinfo \
+                ghostscript6 netpbm m4 gettext
+@end example        
+
+and:
+                
+@example
+        fink install tetex
+@end example        
+
+@c  brokenness of autoconf; don't ask
+Then, configure, patch, make and install LilyPond using these commands:
+
+@example
+        CC="cc -I/sw/include" CXX="c++ -I/sw/include" LDFLAGS="-L/sw/lib" \
+            ./configure --prefix=/sw
+        make -C lily out/parser.hh out/parser.cc
+        patch -p0 < darwin.patch
+        make -C lily out/parser.o
+        make all
+        make install
+@end example
+
+For installing, you must be root, of course.
 
 @c Why isn't this in BUGS (where it belongs?)
 @section Problems
index 9b7cdd7ed328fba1fdb3cce703539f58b78c0ff4..35a549cd8cd3248d7512e9fa6a1374ebe1101ac0 100644 (file)
@@ -20,7 +20,7 @@ SCRIPTS = configure aclocal.m4
 README_FILES =  ChangeLog CHANGES COPYING DEDICATION NEWS README.mandrake ROADMAP
 README_TXT_FILES = AUTHORS.txt README.txt INSTALL.txt FAQ.txt
 IN_FILES := $(wildcard *.in)
-EXTRA_DIST_FILES = $(wildcard *.el) vimrc VERSION $(README_FILES)  $(SCRIPTS) $(IN_FILES)  emacsclient.patch lexer-gcc-3.0.patch .cvsignore
+EXTRA_DIST_FILES = $(wildcard *.el) vimrc VERSION $(README_FILES)  $(SCRIPTS) $(IN_FILES)  emacsclient.patch lexer-gcc-3.0.patch darwin.patch .cvsignore
 NON_ESSENTIAL_DIST_FILES = $(README_TXT_FILES)
 INSTALLATION_DIR=$(datadir)
 INSTALLATION_FILES=$(configuration) VERSION
diff --git a/VERSION b/VERSION
index bf9aae5ee8f9a2c3103b4a0cd2d877921e5bb5a5..f20360b40ce231d7ad76a80522f28e27a3d9a9dc 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -2,7 +2,7 @@ PACKAGE_NAME=LilyPond
 MAJOR_VERSION=1
 MINOR_VERSION=5
 PATCH_LEVEL=38
-MY_PATCH_LEVEL=rz1
+MY_PATCH_LEVEL=jcn2
 
 # use the above to send patches: MY_PATCH_LEVEL is always empty for a
 # released version.
diff --git a/darwin.patch b/darwin.patch
new file mode 100644 (file)
index 0000000..f50c32d
--- /dev/null
@@ -0,0 +1,32 @@
+--- lily/out/parser.hh.orig    Tue Mar 12 16:10:02 2002
++++ lily/out/parser.hh Tue Mar 12 16:23:58 2002
+@@ -14,7 +14,7 @@
+     Request * request;
+-    //* We use SCMs to do strings, because it saves us the trouble of
++    /* We use SCMs to do strings, because it saves us the trouble of
+ deleting them.  Let's hope that a stack overflow doesnt trigger a move
+ of the parse stack onto the heap. */
+--- lily/out/parser.cc.orig    Tue Mar 12 16:08:40 2002
++++ lily/out/parser.cc Tue Mar 12 16:16:02 2002
+@@ -227,7 +227,7 @@
+ // needed for bison.simple's malloc () and free ()
+-#include <malloc.h>
++#include <stdlib.h>
+ #ifndef NDEBUG
+ #define YYDEBUG 1
+@@ -258,7 +258,7 @@
+     Request * request;
+-    //* We use SCMs to do strings, because it saves us the trouble of
++    /* We use SCMs to do strings, because it saves us the trouble of
+ deleting them.  Let's hope that a stack overflow doesnt trigger a move
+ of the parse stack onto the heap. */
+     SCM scm;
index 4c41c9cd96dfb9a34f5936f3ba007f30e8effad6..f1ab03568e22a2f1f188524b81400e7283e895c2 100644 (file)
@@ -1,14 +1,14 @@
 
-% #(set! point-and-click line-column-location)
+%% #(set! point-and-click line-column-location)
 
 \header {
-title = "Solo Cello Suite II"
-piece ="Sarabande"
-composer = "J.S.Bach"
-editor = "August Wenzinger"
-source= "B\\\"arenreiter Urtext"
+  title = "Solo Cello Suite II"
+  piece ="Sarabande"
+  composer = "J.S.Bach"
+  editor = "August Wenzinger"
+  source= "B\\\"arenreiter Urtext"
 
-texidoc = "The B\\\"arenreiter edition of the Cello Suites is the most
+  texidoc = "The B\\\"arenreiter edition of the Cello Suites is the most
 beautifully typeset piece of music in our collection of music (we both
 own one. It is also lovely on French Horn). This piece follows the
 same beaming as the printed edition. This is done in order to
@@ -26,109 +26,125 @@ beam and slur handling."
 
 
 sarabandeA =  \context Voice \notes \relative c {
-    \property Staff.NoteCollision \set #'merge-differently-dotted = ##t
-       < { d8. e16 e4.-\trill d16 e } \\
-         { d4 a2 } >
-       f4.  [e8 d c] |
-       [bes g'] [f e16(f] [g a bes)d,] |
-       cis4.-\trill [b8 a g] |
-
-% check spacing without accs: 
-%      c4.-\trill [bes8 a g] |
-       
-       < { d'8. e16 f4.-\trill d16 e |
-           f4. [d8 e f] }
-         \\
-         { <a,4 f> a2 <a4. d,4.>  } > |
-       %5
-
-       g8 bes16()a c()bes a()g d'8 f, |
-       <  e4.-\trill
-          \\ <a,,4 e'> >
-         [d8 c bes]
-       %8
-       < { f'8 g16()a a4. g16()f  |
-            g8 a16()bes bes4. c16()d }
-         \\
-         { a,4 <bes4. d4. > r8 bes4 <g2 f'2>  }
-       > |
-
-       % 11
-        [e,8 f] [c, g'] [f' e] |
-       f4 f,2 |
-       < {  a'4 a4.-\trill bes8 
-            c bes16 a } \\
-         { [f8 es] es4. r8 d4 } >
-
-       fis8.-\trill es16 d8 c |
-       [bes g'] [a, fis'] [es' d] |
-       %16
-       < bes4.-\trill d, g, > [a8 g f!] |
-       e bes a f' g a |
-       d, as g es' f g |
-       [cis, bes'] [a g16 f] [e!8 f16 d] |
-       cis8 e16 a a,8. g'16 f8()e |
-       %21
-       < { d e16()f f4. e16()d |
-           e8 f16()g g4. a16()bes |
-           a8 cis16 d d,8 e16 f32 g f8-\trill e16()d } \\
-         { bes4 g2 |
-           g4 <bes4. cis,> s8 |
-           <d8 a f> r r g, a4 } >
-       |
-       d4 d,16 a'( b cis d e f )g |
-    \break
-       %25
-       < { a16(b c)b c4. b16()a |
-           b cis d cis d4. e16()f | }
-         \\
-         { f,4 fis4. s8 |
-           <d4 g,> gis4.   } >
-       d16(cis)d f,  [a,8 e'] [d' cis] |
-       d4 d,,2 |
+  \property Staff.NoteCollision \set #'merge-differently-dotted = ##t
+  \property Voice.Beam \set #'quantise-dy-never-steeper = ##t
+  \property Voice.Beam \set #'ideal-lengthen = ##t
+  
+  < { d8. e16 e4.-\trill d16 e } \\
+    { d4 a2 } >
+  f4.  [e8 d c] |
+  [bes g'] [f e16(f] [g a bes)d,] |
+  cis4.-\trill [b8 a g] |
+
+  %% check spacing without accs: 
+  %%   c4.-\trill [bes8 a g] |
+  
+  < { d'8. e16 f4.-\trill d16 e |
+      f4. [d8 e f] }
+    \\
+    { <a,4 f> a2 <a4. d,4.>  } > |
+  %%5
+
+  g8 bes16()a c()bes a()g d'8 f, |
+  <  e4.-\trill
+     \\ <a,,4 e'> >
+  [d8 c bes]
+  %%8
+  < { f'8 g16()a a4. g16()f  |
+      g8 a16()bes bes4. c16()d }
+    \\
+    { a,4 <bes4. d4. > r8 bes4 <g2 f'2>  }
+  > |
+
+  %% 11
+  [e,8 f] [c, g'] [f' e] |
+  f4 f,2 |
+  < {  a'4 a4.-\trill bes8 
+       c bes16 a } \\
+    { [f8 es] es4. r8 d4 } >
+
+  fis8.-\trill es16 d8 c |
+  [bes g']
+  \stemUp
+  [a, fis']
+  \stemBoth
+  [es' d] |
+  %%16
+  < bes4.-\trill d, g, > [a8 g f!] |
+  e bes a f' g a |
+  d, as g es' f g |
+  [cis, bes'] [a g16 f] [e!8 f16 d] |
+  cis8 e16 a a,8. g'16 f8()e |
+  %%21
+  < { d e16()f f4. e16()d |
+      e8 f16()g g4. a16()bes |
+      a8 cis16 d d,8 e16 f32 g f8-\trill e16()d } \\
+    { bes4 g2 |
+      g4 <bes4. cis,> s8 |
+      <d8 a f> r r g, a4 } >
+  |
+  \stemUp
+  d4 d,16 a'( b cis d e f )g |
+  \stemBoth
+  \break
+  %%25
+  < { a16(b c)b c4. b16()a |
+      b cis d cis d4. e16()f | }
+    \\
+    { f,4 fis4. s8 |
+      <d4 g,> gis4.   } >
+  \stemUp
+  d16(cis)d f,
+  [a,8 e']
+  \stemBoth
+  [d' cis] |
+  d4 d,,2 |
 }
 
 
 sarabande =  \context Staff \notes<
-       \apply #voicify-music \sarabandeA
-       
+  \apply #voicify-music \sarabandeA
+  
 >
 
 \version "1.3.148"
 
 sarabandeCelloGlobal =  \notes{
-       \time 3/4
-       \key f \major
-       \clef bass
-       \repeat "volta" 2 {
-               s2.*12
-       } \repeat "volta" 2 {
-               s2.*16
-       }
+  \time 3/4
+  \key f \major
+  \clef bass
+  \repeat "volta" 2 {
+    s2.*12
+  } \repeat "volta" 2 {
+    s2.*16
+  }
 }
 
 sarabandeCelloScripts =  \notes{
 }
 
 sarabandeCelloStaff =  \context Staff <
-       \sarabande
-       \sarabandeCelloGlobal
-       \sarabandeCelloScripts
+  \sarabande
+  \sarabandeCelloGlobal
+  \sarabandeCelloScripts
 >
 
 \score{
-       \sarabandeCelloStaff
-       \paper{
-           indent = 7. \mm
-           linewidth = 183.5 \mm
-       \translator { \ScoreContext
-%              SpacingSpanner \override #'maximum-duration-for-spacing = #(make-moment 1 16)
-
-
-}}
-       \midi{ \tempo 4 = 40 }
-       \header{
-       opus= "" 
-       piece ="Sarabande" }
+  \sarabandeCelloStaff
+  \paper{
+    indent = 7. \mm
+    linewidth = 183.5 \mm
+    \translator { \ScoreContext
+                 %% SpacingSpanner \override #'maximum-duration-for-spacing = #(make-moment 1 16)
+
+
+               }}
+  \midi{ \tempo 4 = 40 }
+  \header{
+    opus= "" 
+    piece ="Sarabande" }
 }
 
+%%% Local variables:
+%%% LilyPond-indent-level:2
+%%% End:
index f7de3f18af72a5e585e679b3d7ff391987b5a218..e0440d5ec53d96b5ca3b327282973147cec42a94 100644 (file)
@@ -267,8 +267,6 @@ Beam::set_stem_shorten (Grob*m)
   Spanner*me = dynamic_cast<Spanner*> (m);
 
   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
-  if (forced_fraction < 0.5)
-    return;
 
   int multiplicity = get_multiplicity (me);
 
@@ -282,21 +280,10 @@ Beam::set_stem_shorten (Grob*m)
   SCM shorten_elt = scm_list_ref (shorten, gh_int2scm (multiplicity <? (sz - 1)));
   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
 
-  /* cute, but who invented me -- how to customise ? */
-  if (forced_fraction < 1)
-    shorten_f /= 2;
-
-  Link_array<Item> stems=
-    Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
-
-  for (int i=0; i < stems.size (); i++)
-    {
-      Item* s = stems[i];
-      if (Stem::invisible_b (s))
-        continue;
-      if (gh_number_p (s->get_grob_property ("shorten")))
-       s->set_grob_property ("shorten", gh_double2scm (shorten_f));
-    }
+  /* your similar cute comment here */
+  shorten_f *= forced_fraction;
+  
+  me->set_grob_property ("shorten", gh_double2scm (shorten_f));
 }
 
 /*
@@ -409,6 +396,8 @@ Beam::check_concave (SCM smob)
   if (stems.size () < 3)
     return SCM_UNSPECIFIED;
 
+  /* TODO: find-out what makes beam concave (#1, #2, #3, #4 or
+     something else) */
   SCM s = me->get_grob_property ("concaveness-no-slope");
 
   Real concave = 0;
@@ -416,6 +405,8 @@ Beam::check_concave (SCM smob)
     {
       /* Concaveness try #1: Sum distances of inner noteheads to line
         between two outer noteheads.  */
+
+      s = me->get_grob_property ("concave-if-bigger-than-two");
       
       Real dy = Stem::chord_start_f (stems.top ())
        - Stem::chord_start_f (stems[0]);
@@ -425,6 +416,19 @@ Beam::check_concave (SCM smob)
       for (int i = 1; i < stems.size () - 1; i++)
        {
          Real c = (Stem::chord_start_f (stems[i]) - y0) - i * slope;
+
+         /* try #4: (Han-Wen): neem maximum afstand lijn - tot
+            extreme notehead (in geval van akkoorden). Als die
+            afstand >= 2.0 ss was, dan moest hij recht (of blijkbaar:
+            vrijwel recht, zie m 17, 18). Dat was nl. wat stolba zei:
+            als afstand lijn-noot >= 2.0 dan recht. */
+         
+         if (to_boolean (s) && c >= 2.0)
+           {
+             concave = 1000 * Directional_element_interface::get (me);
+             break;
+           }
+         
          concave += c;
        }
 
@@ -457,7 +461,9 @@ Beam::check_concave (SCM smob)
   Real concaveness = concave / (stems.size () - 2);
 
   /* ugh: this is the a kludge to get input/regression/beam-concave.ly
-     to behave as baerenreiter. */
+     to behave as baerenreiter.
+
+    try #3 (add-on to #2): */
   s = me->get_grob_property ("concaveness-square");
   if (to_boolean (s))
     concaveness /= (stems.size () - 2);
@@ -465,6 +471,7 @@ Beam::check_concave (SCM smob)
   s = me->get_grob_property ("concaveness");
   Real r = gh_scm2double (s);
 
+  /* TODO: some sort of damping iso -> plain horizontal */
   if (concaveness > r)
     {
       Direction dir = Directional_element_interface::get (me);
@@ -567,9 +574,13 @@ Beam::quantise_dy (SCM smob)
       Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
        ? iv[SMALLER]
        : iv[BIGGER];
-      
+
+      if (to_boolean (me->get_grob_property ("quantise-dy-never-steeper"))
+         && iv[SMALLER] != 0)
+       q = iv[SMALLER];
+         
       Real quantised_dy = q * sign (dy);
-      Real adjusted_y = y + (dy - quantised_dy) / 2;
+      Real adjusted_y = y + (dy - quantised_dy) * 0.5;
       /* Store true, not dir-corrected values */
       me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
       me->set_grob_property ("dy", gh_double2scm (quantised_dy * dir));
@@ -699,6 +710,11 @@ Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy)
   return stem_y;
 }
 
+/* Make very sure that we don't have stems that are too short.
+   Try our best not to have stems that are too long (think: knees).
+   
+   Optionally (testing): try to lengthen more, to reach more ideal
+   stem lengths */
 Real
 Beam::check_stem_length_f (Grob*me,Real y, Real dy) 
 {
@@ -709,6 +725,9 @@ Beam::check_stem_length_f (Grob*me,Real y, Real dy)
   Link_array<Item> stems=
     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
 
+  int ideal_lengthen_count = 0;
+  Real ideal_lengthen = 0;
+  
   for (int i=0; i < stems.size (); i++)
     {
       Item* s = stems[i];
@@ -723,12 +742,28 @@ Beam::check_stem_length_f (Grob*me,Real y, Real dy)
       // if (0 > info.maxy_f_ - stem_y)
       shorten = shorten <? info.maxy_f_ - stem_y;
       // if (0 < info.miny_f_ - stem_y)
-      lengthen = lengthen >? info.miny_f_ - stem_y; 
+      lengthen = lengthen >? info.miny_f_ - stem_y;
+
+      if (info.idealy_f_ - stem_y > 0)
+       {
+         ideal_lengthen += (info.idealy_f_ - stem_y);
+         ideal_lengthen_count++;
+       }
+      // too long is not so bad as too short
+      else if (0) //info.idealy_f_ - stem_y < 0)
+       {
+         ideal_lengthen += info.idealy_f_ - stem_y;
+         ideal_lengthen_count++;
+       }
     }
 
   if (lengthen && shorten)
     me->warning (_ ("weird beam vertical offset"));
 
+  if (to_boolean (me->get_grob_property ("ideal-lengthen"))
+      && ideal_lengthen_count)
+    lengthen = (ideal_lengthen / ideal_lengthen_count) >? lengthen;
+      
   /* when all stems are too short, normal stems win */
   return dir * ((shorten) ?  shorten : lengthen);
 }
index 456f0e48994d083b71dc444980fa868e983ba2d9..e69de92f549e16faf7cce22445d4bb20454883db 100644 (file)
@@ -296,6 +296,10 @@ Stem::get_default_stem_end_position (Grob*me)
   // fixme: use scm_list_n_ref () iso. array[]
   Real shorten_f = a[ ((flag_i (me) - 2) >? 0) <? (a.size () - 1)] * 2;
 
+  /* On boundary: shorten only half */
+  if (abs (chord_start_f (me)) == 0.5)
+    shorten_f *= 0.5;
+
   /* URGURGURG
      'set-default-stemlen' sets direction too
    */
@@ -306,11 +310,9 @@ Stem::get_default_stem_end_position (Grob*me)
       Directional_element_interface::set (me, dir);
     }
   
-  /* 
-    stems in unnatural (forced) direction should be shortened, 
-    according to [Roush & Gourlay]
-   */
-  if (( (int)chord_start_f (me))
+  /* stems in unnatural (forced) direction should be shortened, 
+    according to [Roush & Gourlay] */
+  if (chord_start_f (me)
       && (get_direction (me) != get_default_dir (me)))
     length_f -= shorten_f;
 
index 7a54bdca2c9ac58b005260c9c0b69b7ae50c6dc2..d441d1aa37eea700e58bf3c07dcc11e2c34875ba 100644 (file)
        (concaveness . 0.08)
        (concaveness-no-slope . #t)
        (concaveness-square . #t)
+       (ideal-lengthen . #t)
        (y-dy-callbacks . (,Beam::least_squares
                           ,Beam::check_concave
                           ,Beam::slope_damping
        (dir-function . ,beam-dir-majority)
        (height-quants .  ,default-beam-dy-quants)
        (vertical-position-quant-function . ,default-beam-y-quants)
-       (beamed-stem-shorten . (0.5))
+       (beamed-stem-shorten . (1.0 0.5))
        (outer-stem-length-limit . 0.2)
        (slope-limit . 0.2)
        (flag-width-function . ,default-beam-flag-width-function)
        ;;  a whole staffspace seems a bit drastical: we'll do half.
 
        (lengths . (3.5 3.5 3.5 4.5 5.0))
-       (stem-shorten . (0.5))
+       (stem-shorten . (1.0 0.5))
                                        ; if stem is on middle line, choose this direction.
        (neutral-direction . -1)
        (X-offset-callbacks . (,Stem::off_callback))