1 /*
2 This file is part of LilyPond, the GNU music typesetter.
3
4 Copyright (C) 1998--2020 Jan Nieuwenhuizen <janneke@gnu.org>
5
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "engraver.hh"
21
22 #include "context.hh"
23 #include "duration.hh"
24 #include "grob-array.hh"
25 #include "item.hh"
26 #include "music.hh"
27 #include "stream-event.hh"
28 #include "text-interface.hh"
29
30 #include "translator.icc"
31
32 #include <cctype>
33
34 class Metronome_mark_engraver : public Engraver
35 {
36 Item *text_;
37 Grob *support_;
38 Grob *bar_;
39 Stream_event *tempo_ev_;
40
41 public:
42 TRANSLATOR_DECLARATIONS (Metronome_mark_engraver);
43
44 protected:
45 void stop_translation_timestep ();
46 void process_music ();
47
48 void acknowledge_break_aligned (Grob_info);
49 void acknowledge_break_alignment (Grob_info);
50 void acknowledge_grob (Grob_info) override;
51
52 void listen_tempo_change (Stream_event *);
53 };
54
Metronome_mark_engraver(Context * c)55 Metronome_mark_engraver::Metronome_mark_engraver (Context *c)
56 : Engraver (c)
57 {
58 text_ = 0;
59 support_ = 0;
60 bar_ = 0;
61 tempo_ev_ = 0;
62 }
63
64 void
listen_tempo_change(Stream_event * ev)65 Metronome_mark_engraver::listen_tempo_change (Stream_event *ev)
66 {
67 ASSIGN_EVENT_ONCE (tempo_ev_, ev);
68 }
69
70 static bool
safe_is_member(SCM scm,SCM lst)71 safe_is_member (SCM scm, SCM lst)
72 {
73 return ly_is_list (lst) && scm_is_true (scm_member (scm, lst));
74 }
75
76 void
acknowledge_break_aligned(Grob_info info)77 Metronome_mark_engraver::acknowledge_break_aligned (Grob_info info)
78 {
79 Grob *g = info.grob ();
80
81 if (text_
82 && scm_is_eq (get_property (g, "break-align-symbol"),
83 ly_symbol2scm ("staff-bar")))
84 bar_ = g;
85 else if (text_ && !support_
86 && safe_is_member (get_property (g, "break-align-symbol"),
87 get_property (text_, "break-align-symbols")))
88 {
89 support_ = g;
90 text_->set_x_parent (g);
91 }
92 if (bar_ || support_)
93 set_property (text_, "non-musical", SCM_BOOL_T);
94 }
95
96 void
acknowledge_break_alignment(Grob_info info)97 Metronome_mark_engraver::acknowledge_break_alignment (Grob_info info)
98 {
99 Grob *g = info.grob ();
100
101 if (text_
102 && support_
103 && dynamic_cast<Item *> (g))
104 text_->set_x_parent (g);
105 }
106
107 void
acknowledge_grob(Grob_info info)108 Metronome_mark_engraver::acknowledge_grob (Grob_info info)
109 {
110 Grob *g = info.grob ();
111
112 if (text_)
113 for (SCM s = get_property (text_, "non-break-align-symbols");
114 scm_is_pair (s);
115 s = scm_cdr (s))
116 if (g->internal_has_interface (scm_car (s)))
117 text_->set_x_parent (g);
118 }
119
120 void
stop_translation_timestep()121 Metronome_mark_engraver::stop_translation_timestep ()
122 {
123 if (text_)
124 {
125 if (text_->get_x_parent ()
126 && text_->get_x_parent ()->internal_has_interface (ly_symbol2scm ("multi-measure-rest-interface"))
127 && bar_)
128 text_->set_x_parent (bar_);
129 else if (!support_)
130 {
131 /*
132 Gardner Read "Music Notation", p.278
133
134 Align the metronome mark over the time signature (or the
135 first notational element of the measure if no time
136 signature is present in that measure).
137 */
138 if (Grob *mc = unsmob<Grob> (get_property (this, "currentMusicalColumn")))
139 text_->set_x_parent (mc);
140 else if (Grob *cc = unsmob<Grob> (get_property (this, "currentCommandColumn")))
141 text_->set_x_parent (cc);
142 }
143 set_object (text_, "side-support-elements",
144 grob_list_to_grob_array (get_property (this, "stavesFound")));
145 text_ = 0;
146 support_ = 0;
147 bar_ = 0;
148 tempo_ev_ = 0;
149 }
150 }
151
152 void
process_music()153 Metronome_mark_engraver::process_music ()
154 {
155 if (tempo_ev_)
156 {
157 text_ = make_item ("MetronomeMark", tempo_ev_->self_scm ());
158
159 SCM proc = get_property (this, "metronomeMarkFormatter");
160 SCM result = scm_call_2 (proc,
161 tempo_ev_->self_scm (),
162 context ()->self_scm ());
163
164 set_property (text_, "text", result);
165 }
166 }
167
168 void
boot()169 Metronome_mark_engraver::boot ()
170 {
171 ADD_LISTENER (Metronome_mark_engraver, tempo_change);
172 ADD_ACKNOWLEDGER (Metronome_mark_engraver, break_aligned);
173 ADD_ACKNOWLEDGER (Metronome_mark_engraver, break_alignment);
174 ADD_ACKNOWLEDGER (Metronome_mark_engraver, grob);
175 }
176
177 ADD_TRANSLATOR (Metronome_mark_engraver,
178 /* doc */
179 "Engrave metronome marking. This delegates the formatting"
180 " work to the function in the @code{metronomeMarkFormatter}"
181 " property. The mark is put over all staves. The staves are"
182 " taken from the @code{stavesFound} property, which is"
183 " maintained by @ref{Staff_collecting_engraver}.",
184
185 /* create */
186 "MetronomeMark ",
187
188 /* read */
189 "currentCommandColumn "
190 "currentMusicalColumn "
191 "metronomeMarkFormatter "
192 "stavesFound "
193 "tempoHideNote ",
194
195 /* write */
196 ""
197 );
198