]> git.donarmstrong.com Git - lilypond.git/commitdiff
New engraver for braces.
authorBertrand Bordage <bordage.bertrand@gmail.com>
Thu, 4 Aug 2011 12:56:50 +0000 (14:56 +0200)
committerBertrand Bordage <bordage.bertrand@gmail.com>
Sun, 21 Aug 2011 14:29:38 +0000 (16:29 +0200)
15 files changed:
input/regression/braces.ly [new file with mode: 0644]
lily/arpeggio.cc
lily/brace-engraver.cc [new file with mode: 0644]
lily/include/arpeggio.hh
lily/span-brace-engraver.cc [new file with mode: 0644]
ly/engraver-init.ly
ly/property-init.ly
scm/define-context-properties.scm
scm/define-event-classes.scm
scm/define-grob-interfaces.scm
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/define-music-display-methods.scm
scm/define-music-types.scm
scm/safe-lily.scm

diff --git a/input/regression/braces.ly b/input/regression/braces.ly
new file mode 100644 (file)
index 0000000..7560b52
--- /dev/null
@@ -0,0 +1,29 @@
+\version "2.15.6"
+
+\header{
+  texidoc="
+Braces can be used to show organ keyboard changes.
+"
+}
+
+\score {
+  <<
+    \new PianoStaff <<
+      { << { d''2~\brace d''~ d'' } \\ { s1 <a d' f'>2\brace a' } >> }
+      \new Dynamics { s1-\markup \bold \upright "G.O." }
+      { f8\brace
+        \once \override Brace #'minimum-brace-height = #1
+        \once \override Brace #'positions = #'(-5.5 . 0)
+        <a' c' e'>\brace-\markup \bold "Pos." a' a' a'2\brace f'\brace f' }
+    >>
+    \new Staff { \clef F R1 d2\brace d }
+  >>
+  \layout {
+    ragged-right = ##t
+    \context {
+      \Score
+      \consists Span_brace_engraver
+      connectBraces = ##t
+    }
+  }
+}
index 9b85808a263a93831916d766dce1c09a21dc003d..3cdfe4c06ab5c88ef5995897fc904165701bb4b5 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "arpeggio.hh"
 
+#include "all-font-metrics.hh"
 #include "bezier.hh"
 #include "font-interface.hh"
 #include "grob.hh"
@@ -175,6 +176,45 @@ Arpeggio::brew_chord_bracket (SCM smob)
   return mol.smobbed_copy ();
 }
 
+MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_brace, 1);
+SCM
+Arpeggio::brew_chord_brace (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  Interval heads = robust_scm2interval (me->get_property ("positions"),
+                                        Interval ())
+    * Staff_symbol_referencer::staff_space (me);
+  int minimum_brace_height = robust_scm2int (
+                             me->get_property ("minimum-brace-height"), 1);
+  Font_metric *fm = Font_interface::get_default_font (me);
+
+  int
+    lo = 0;
+  int hi = max ((int) fm->count () - 1, 2);
+
+  /* do a binary search for each Y, not very efficient, but passable?  */
+  Box b;
+  do
+    {
+      int cmp = (lo + hi) / 2;
+      b = fm->get_indexed_char_dimensions (cmp);
+      if (b[Y_AXIS].is_empty () || b[Y_AXIS].length () > heads.length ()+1)
+        hi = cmp;
+      else
+        lo = cmp;
+    }
+  while (hi - lo > 1);
+
+  if (lo < minimum_brace_height)
+    lo = minimum_brace_height;
+
+  Stencil mol (unsmob_metrics (me->get_property ("font"))
+                              ->find_by_name ("brace" + to_string (lo)));
+  mol.translate_axis ((heads[RIGHT] + heads[LEFT]) / 2, Y_AXIS);
+
+  return mol.smobbed_copy ();
+}
+
 MAKE_SCHEME_CALLBACK (Arpeggio, brew_chord_slur, 1);
 SCM
 Arpeggio::brew_chord_slur (SCM smob)
@@ -226,6 +266,7 @@ ADD_INTERFACE (Arpeggio,
 
                /* properties */
                "arpeggio-direction "
+               "minimum-brace-height "
                "positions "
                "script-priority " // TODO: make around-note-interface
                "stems "
diff --git a/lily/brace-engraver.cc b/lily/brace-engraver.cc
new file mode 100644 (file)
index 0000000..15acdf0
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Bertrand Bordage
+                     Mike Solomon
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "engraver.hh"
+
+#include "pointer-group-interface.hh"
+#include "arpeggio.hh"
+#include "stem.hh"
+#include "rhythmic-head.hh"
+#include "side-position-interface.hh"
+#include "stream-event.hh"
+#include "note-column.hh"
+#include "item.hh"
+
+#include "translator.icc"
+
+class Brace_engraver : public Engraver
+{
+public:
+  TRANSLATOR_DECLARATIONS (Brace_engraver);
+
+  void acknowledge_stem (Grob_info);
+  void acknowledge_rhythmic_head (Grob_info);
+protected:
+  void process_music ();
+  void stop_translation_timestep ();
+  DECLARE_TRANSLATOR_LISTENER (brace);
+
+private:
+  Item *brace_;
+  Stream_event *brace_event_;
+};
+
+Brace_engraver::Brace_engraver ()
+{
+  brace_ = 0;
+  brace_event_ = 0;
+}
+
+IMPLEMENT_TRANSLATOR_LISTENER (Brace_engraver, brace);
+void Brace_engraver::listen_brace (Stream_event *ev)
+{
+  ASSIGN_EVENT_ONCE (brace_event_, ev);
+}
+
+void
+Brace_engraver::acknowledge_stem (Grob_info info)
+{
+  if (brace_)
+    {
+      if (!brace_->get_parent (Y_AXIS))
+        brace_->set_parent (info.grob (), Y_AXIS);
+
+      Pointer_group_interface::add_grob (brace_,
+                                         ly_symbol2scm ("stems"),
+                                         info.grob ());
+    }
+}
+void
+Brace_engraver::acknowledge_rhythmic_head (Grob_info info)
+{
+  if (brace_)
+
+    /*
+      We can't catch local key items (accidentals) from Voice context,
+      see Local_key_engraver
+    */
+    Side_position_interface::add_support (brace_, info.grob ());
+}
+
+void
+Brace_engraver::process_music ()
+{
+  if (brace_event_)
+    brace_ = make_item ("Brace", brace_event_->self_scm ());
+}
+
+void
+Brace_engraver::stop_translation_timestep ()
+{
+  brace_ = 0;
+  brace_event_ = 0;
+}
+
+ADD_ACKNOWLEDGER (Brace_engraver, stem);
+ADD_ACKNOWLEDGER (Brace_engraver, rhythmic_head);
+
+ADD_TRANSLATOR (Brace_engraver,
+                /* doc */
+                "Generate a Brace symbol.",
+
+                /* create */
+                "Brace",
+
+                /* read */
+                "",
+
+                /* write */
+                ""
+                );
+
index f9096432c4c5b43c0a3d47c4c4935a5c7c76401a..55ceb06b807281e97401623f924d278304ef61f1 100644 (file)
@@ -30,6 +30,7 @@ public:
   DECLARE_SCHEME_CALLBACK (print, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_positions, (SCM));
   DECLARE_SCHEME_CALLBACK (brew_chord_bracket, (SCM));
+  DECLARE_SCHEME_CALLBACK (brew_chord_brace, (SCM));
   DECLARE_SCHEME_CALLBACK (brew_chord_slur, (SCM));
   DECLARE_SCHEME_CALLBACK (width, (SCM));
   DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM));
diff --git a/lily/span-brace-engraver.cc b/lily/span-brace-engraver.cc
new file mode 100644 (file)
index 0000000..4965188
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Bertrand Bordage
+                     Mike Solomon
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "engraver.hh"
+#include "arpeggio.hh"
+#include "pointer-group-interface.hh"
+#include "side-position-interface.hh"
+#include "staff-symbol-referencer.hh"
+#include "item.hh"
+
+/**
+   Make braces that span multiple staves.  Catch braces, and span a
+   Span_brace over them if we find more than two braces.
+*/
+class Span_brace_engraver : public Engraver
+{
+public:
+  TRANSLATOR_DECLARATIONS (Span_brace_engraver);
+  DECLARE_ACKNOWLEDGER (brace);
+
+protected:
+  void process_acknowledged ();
+  void stop_translation_timestep ();
+
+private:
+  Item *span_brace_;
+  vector<Grob*> braces_;
+};
+
+Span_brace_engraver::Span_brace_engraver ()
+{
+  span_brace_ = 0;
+}
+
+void
+Span_brace_engraver::acknowledge_brace (Grob_info info)
+{
+  if (info.origin_contexts (this).size ()) // huh? what's this test for?
+    braces_.push_back (info.grob ());
+}
+
+void
+Span_brace_engraver::process_acknowledged ()
+{
+  /*
+    connectBraces is slightly brusque; we should really read a grob
+    property of the caught non-span braces. That way, we can have
+
+    both non-connected and connected braces in one pianostaff.
+
+  */
+  if (!span_brace_ && braces_.size () > 1
+      && to_boolean (get_property ("connectBraces")))
+    {
+      span_brace_ = make_item ("Brace", SCM_EOL);
+      span_brace_->set_property ("cross-staff", SCM_BOOL_T);
+    }
+}
+
+void
+Span_brace_engraver::stop_translation_timestep ()
+{
+  if (span_brace_)
+    {
+      /*
+        we do this very late, to make sure we also catch `extra'
+        side-pos support like accidentals.
+      */
+      for (vsize j = 0; j < braces_.size (); j++)
+        {
+          extract_grob_set (braces_[j], "stems", stems);
+          for (vsize i = 0; i < stems.size (); i++)
+            Pointer_group_interface::add_grob (span_brace_, ly_symbol2scm ("stems"),
+                                               stems[i]);
+
+          extract_grob_set (braces_[j], "side-support-elements", sses);
+          for (vsize i = 0; i < sses.size (); i++)
+            Pointer_group_interface::add_grob (span_brace_, ly_symbol2scm ("side-support-elements"),
+                                               sses[i]);
+
+          /*
+            we can't kill the children, since we don't want to the
+            previous note to bump into the span brace; so we make
+            it transparent.  */
+          braces_[j]->set_property ("transparent", SCM_BOOL_T);
+        }
+
+
+      span_brace_->set_parent (braces_[0]->get_parent (Y_AXIS), Y_AXIS);
+      span_brace_ = 0;
+    }
+  braces_.clear ();
+}
+
+#include "translator.icc"
+
+ADD_ACKNOWLEDGER (Span_brace_engraver, brace);
+ADD_TRANSLATOR (Span_brace_engraver,
+                /* doc */
+                "Make braces that span multiple staves.",
+
+                /* create */
+                "Brace ",
+
+                /* read */
+                "connectBraces ",
+
+                /* write */
+                ""
+                );
index badb701e08579e8aa7bb4f70ec8b027eaaae0380..d9f95f572fef2db9bb3bff44c12fa63738985c34 100644 (file)
@@ -213,6 +213,7 @@ multiple voices on the same staff."
   \consists "Pitched_trill_engraver"
   \consists "Output_property_engraver"
   \consists "Arpeggio_engraver"
+  \consists "Brace_engraver"
   \consists "Multi_measure_rest_engraver"
   \consists "Text_spanner_engraver"
   \consists "Trill_spanner_engraver"
@@ -287,6 +288,7 @@ multiple voices on the same staff."
 
   \description "A voice on a percussion staff."
   \remove "Arpeggio_engraver"
+  \remove "Brace_engraver"
   \consists "Grob_pq_engraver"
 
   \remove "Note_head_line_engraver"
@@ -313,6 +315,7 @@ contained staves are connected vertically."
   \consists "Instrument_name_engraver"
   \consists "Span_bar_engraver"
   \consists "Span_arpeggio_engraver"
+  \consists "Span_brace_engraver"
   \consists "System_start_delimiter_engraver"
   \consists "Vertical_align_engraver"
   systemStartDelimiter = #'SystemStartBrace
@@ -358,6 +361,7 @@ together, never separately."
   \consists "Instrument_name_engraver"
   \consists "Span_bar_engraver"
   \consists "Span_arpeggio_engraver"
+  \consists "Span_brace_engraver"
   \consists "Output_property_engraver"
   systemStartDelimiter = #'SystemStartBracket
   %% explicitly set instrument, so it is not inherited from the parent
@@ -861,6 +865,7 @@ contexts and handles the line spacing, the tablature clef etc. properly."
   \override TimeSignature #'stencil = ##f
   %% no arpeggios
   \override Arpeggio #'stencil = ##f
+  \override Brace #'stencil = ##f
   %% we ignore collision warnings that may occur due to
   %% stem overlapping, because we have no stems ;-)
   \override NoteColumn #'ignore-collision = ##t
index 0175bb798fe405134afd05236f81f182001af729..5779acf662835d5fe1c009c1b5493f04eb8fe4e6 100644 (file)
@@ -23,6 +23,8 @@ defaultNoteHeads =
 % cross-staff brackets are desired.
 
 arpeggio = #(make-music 'ArpeggioEvent)
+brace = #(make-music 'BraceEvent)
+
 arpeggioArrowUp = {
   \revert Arpeggio  #'stencil
   \revert Arpeggio #'X-extent
index e9acd9690713e8924844cda7d0ff90f3f565a16c..044bb699730f7f1db7e5a920db26634145904d78 100644 (file)
@@ -163,6 +163,8 @@ staff.")
      (completionBusy ,boolean? "Whether a completion-note head is playing.")
      (connectArpeggios ,boolean? "If set, connect arpeggios across
 piano staff.")
+     (connectBraces ,boolean? "If set, connect braces across
+staves.")
      (countPercentRepeats ,boolean? "If set, produce counters for
 percent repeats.")
      (createKeyOnClefChange ,boolean? "Print a key signature whenever
index c632e430a95f37a1dd184e8a8cdef5e4906ce132..356ab02fcf634ba743825f975601500f0ea8e1ab 100644 (file)
@@ -29,7 +29,7 @@
                    arpeggio-event breathing-event extender-event span-event
       rhythmic-event dynamic-event break-event label-event percent-event
       key-change-event string-number-event stroke-finger-event tie-event
-      part-combine-event part-combine-force-event
+      part-combine-event part-combine-force-event brace-event
       beam-forbid-event script-event tempo-change-event
       tremolo-event bend-after-event fingering-event glissando-event
       harmonic-event hyphen-event laissez-vibrer-event mark-event
index 735b9c78956e4e602f41478525379bb344880232..d0060bbe3075cded00ebae18b43938d74c7a69d5 100644 (file)
@@ -56,6 +56,11 @@ note)."
  "Any kind of loudness sign."
  '())
 
+(ly:add-interface
+ 'brace-interface
+ "A brace."
+ '())
+
 (ly:add-interface
  'dynamic-line-spanner-interface
  "Dynamic line spanner."
index 334245d34996ed0a67b37c29a2dba187047b293a..c0a4c8b929aa3e8196ea7037c014e51fe1737ee2 100644 (file)
@@ -567,6 +567,8 @@ this long, normally in the horizontal direction.  This requires an
 appropriate callback for the @code{springs-and-rods} property.  If
 added to a @code{Tie}, this sets the minimum distance between
 noteheads.")
+     (minimum-brace-height ,integer? "Specifies a minimum height for
+a brace.  The unit is an increment in fetaBraces.")
      (minimum-length-fraction ,number? "Minimum length of ledger line
 as fraction of note head size.")
      (minimum-space ,ly:dimension? "Minimum distance that the victim
index bfe0323b8fd5a2318dbda52cd5041919e447e482..b64c6cc425c996bae8fd65d8c9fd4181bf9449ad 100644 (file)
                                font-interface
                                text-interface))))))
 
+    (Brace
+     . (
+       (direction . ,LEFT)
+       (font-encoding . fetaBraces)
+       (minimum-brace-height . 95)
+       (padding . 0.5)
+       (positions . ,ly:arpeggio::calc-positions)
+       (script-priority . 0)
+       (side-axis . ,X)
+       (staff-position . 0.0)
+       (stencil . ,ly:arpeggio::brew-chord-brace)
+       (X-extent . (-1 . 0))
+       (X-offset . ,ly:side-position-interface::x-aligned-side)
+       (Y-offset . ,ly:staff-symbol-referencer::callback)
+       (meta . ((class . Item)
+                (interfaces . (arpeggio-interface
+                               brace-interface
+                               font-interface
+                               side-position-interface
+                               staff-symbol-referencer-interface))))))
+
     (ChordName
      . (
        (after-line-breaking . ,ly:chord-name::after-line-breaking)
   `(
     (,ly:arpeggio::print . ,ly:arpeggio::pure-height)
     (,ly:arpeggio::brew-chord-bracket . ,ly:arpeggio::pure-height)
+    (,ly:arpeggio::brew-chord-brace . ,ly:arpeggio::pure-height)
     (,ly:arpeggio::brew-chord-slur . ,ly:arpeggio::pure-height)
     (,ly:hairpin::print . ,ly:hairpin::pure-height)
     (,ly:stem-tremolo::print . ,ly:stem-tremolo::pure-height)
index f58d60fa35330990983baf421b6183c9efb64de5..2a160434d6351db8dbae5309ef9169e5b6251af4 100644 (file)
@@ -144,6 +144,7 @@ expression."
     'BeamEvent
     'BeamForbidEvent
     'BendAfterEvent
+    'BraceEvent
     'CrescendoEvent
     'DecrescendoEvent
     'EpisemaEvent
@@ -242,6 +243,7 @@ expression."
   (format #f "\\rightHandFinger #~a" (ly:music-property event 'digit)))
 
 (define-span-event-display-method BeamEvent (event parser) #f "[" "]")
+(define-post-event-display-method BraceEvent (event parser) #t "\\brace")
 (define-span-event-display-method SlurEvent (event parser) #f "(" ")")
 (define-span-event-display-method CrescendoEvent (event parser) #f "\\<" "\\!")
 (define-span-event-display-method DecrescendoEvent (event parser) #f "\\>" "\\!")
index 4085957e032d29178d8b4ccb8bef402d8e843b8c..ac9f5a71b5db293ca24227d5c9acf91946674cbd 100644 (file)
@@ -122,6 +122,13 @@ Syntax: @var{note}@code{\\breathe}")
        (types . (general-music event breathing-event))
        ))
 
+    (BraceEvent
+     . ((description . "Make a brace for this chord.
+
+Syntax: @var{chord}@code{-\\brace}")
+       (types . (general-music brace-event event))
+       ))
+
     (ClusterNoteEvent
      . ((description . "A note that is part of a cluster.")
        ;; not a note-event, to ensure that Note_heads_engraver doesn't eat it.
index 52e4bdeeb7023caa0f7dd348b68551ce08f05682..23dea1975acf059e8acde8bf31e72b4ecb0131ef 100644 (file)
    ly:accidental-interface::print
    ly:arpeggio::print
    ly:arpeggio::brew-chord-bracket
+   ly:arpeggio::brew-chord-brace
    ly:bar-line::print
    ly:breathing-sign::offset-callback
    ly:clef::print