From: Bertrand Bordage Date: Thu, 4 Aug 2011 12:56:50 +0000 (+0200) Subject: New engraver for braces. X-Git-Tag: release/2.15.9-1~9^2~31^2~9 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=97b34b56e2cf3f8465f651717b5e0f6ea10d7543;p=lilypond.git New engraver for braces. --- diff --git a/input/regression/braces.ly b/input/regression/braces.ly new file mode 100644 index 0000000000..7560b525ab --- /dev/null +++ b/input/regression/braces.ly @@ -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 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) + \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 + } + } +} diff --git a/lily/arpeggio.cc b/lily/arpeggio.cc index 9b85808a26..3cdfe4c06a 100644 --- a/lily/arpeggio.cc +++ b/lily/arpeggio.cc @@ -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 index 0000000000..15acdf07ac --- /dev/null +++ b/lily/brace-engraver.cc @@ -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 . +*/ + +#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 */ + "" + ); + diff --git a/lily/include/arpeggio.hh b/lily/include/arpeggio.hh index f9096432c4..55ceb06b80 100644 --- a/lily/include/arpeggio.hh +++ b/lily/include/arpeggio.hh @@ -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 index 0000000000..49651889a2 --- /dev/null +++ b/lily/span-brace-engraver.cc @@ -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 . +*/ + +#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 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 */ + "" + ); diff --git a/ly/engraver-init.ly b/ly/engraver-init.ly index badb701e08..d9f95f572f 100644 --- a/ly/engraver-init.ly +++ b/ly/engraver-init.ly @@ -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 diff --git a/ly/property-init.ly b/ly/property-init.ly index 0175bb798f..5779acf662 100644 --- a/ly/property-init.ly +++ b/ly/property-init.ly @@ -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 diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm index e9acd96907..044bb69973 100644 --- a/scm/define-context-properties.scm +++ b/scm/define-context-properties.scm @@ -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 diff --git a/scm/define-event-classes.scm b/scm/define-event-classes.scm index c632e430a9..356ab02fcf 100644 --- a/scm/define-event-classes.scm +++ b/scm/define-event-classes.scm @@ -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 diff --git a/scm/define-grob-interfaces.scm b/scm/define-grob-interfaces.scm index 735b9c7895..d0060bbe30 100644 --- a/scm/define-grob-interfaces.scm +++ b/scm/define-grob-interfaces.scm @@ -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." diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index 334245d349..c0a4c8b929 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -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 diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index bfe0323b8f..b64c6cc425 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -499,6 +499,27 @@ 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) @@ -2587,6 +2608,7 @@ `( (,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) diff --git a/scm/define-music-display-methods.scm b/scm/define-music-display-methods.scm index f58d60fa35..2a160434d6 100644 --- a/scm/define-music-display-methods.scm +++ b/scm/define-music-display-methods.scm @@ -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 "\\>" "\\!") diff --git a/scm/define-music-types.scm b/scm/define-music-types.scm index 4085957e03..ac9f5a71b5 100644 --- a/scm/define-music-types.scm +++ b/scm/define-music-types.scm @@ -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. diff --git a/scm/safe-lily.scm b/scm/safe-lily.scm index 52e4bdeeb7..23dea1975a 100644 --- a/scm/safe-lily.scm +++ b/scm/safe-lily.scm @@ -155,6 +155,7 @@ 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