]> git.donarmstrong.com Git - lilypond.git/blob - lily/metronome-engraver.cc
Merge branch 'metronome'
[lilypond.git] / lily / metronome-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1998--2010 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 <cctype>
21 using namespace std;
22
23 #include "engraver.hh"
24
25 #include "context.hh"
26 #include "duration.hh"
27 #include "grob-array.hh"
28 #include "item.hh"
29 #include "music.hh"
30 #include "stream-event.hh"
31 #include "text-interface.hh"
32
33 #include "translator.icc"
34
35 class Metronome_mark_engraver : public Engraver
36 {
37 public:
38   TRANSLATOR_DECLARATIONS (Metronome_mark_engraver);
39
40 protected:
41   Item *text_;
42   Grob *support_;
43   Grob *bar_;
44
45   SCM last_duration_;
46   SCM last_count_;
47   SCM last_text_;
48
49   DECLARE_ACKNOWLEDGER (break_aligned);
50   DECLARE_ACKNOWLEDGER (grob);
51
52 protected:
53   virtual void derived_mark () const;
54   void stop_translation_timestep ();
55   void process_music ();
56 };
57
58 Metronome_mark_engraver::Metronome_mark_engraver ()
59 {
60   text_ = 0;
61   support_ = 0;
62   bar_ = 0;
63   last_duration_ = SCM_EOL;
64   last_count_ = SCM_EOL;
65   last_text_ = SCM_EOL;
66 }
67
68 void
69 Metronome_mark_engraver::derived_mark () const
70 {
71   scm_gc_mark (last_count_);
72   scm_gc_mark (last_duration_);
73   scm_gc_mark (last_text_);
74 }
75
76 static bool
77 safe_is_member (SCM scm, SCM lst)
78 {
79   return scm_list_p (lst) == SCM_BOOL_T
80     && scm_member (scm, lst) != SCM_BOOL_F;
81 }
82
83 void
84 Metronome_mark_engraver::acknowledge_break_aligned (Grob_info info)
85 {
86   Grob *g = info.grob ();
87
88   if (text_
89       && g->get_property ("break-align-symbol")
90       == ly_symbol2scm ("staff-bar"))
91       bar_ = g;
92   else if (text_
93            && !support_
94            && safe_is_member (g->get_property ("break-align-symbol"),
95                               text_->get_property ("break-align-symbols")))
96     {
97       support_ = g;
98       text_->set_parent (g, X_AXIS);
99     }
100 }
101
102 SCM
103 grob_name_scm (Grob *g)
104 {
105   SCM name_pair = scm_assq (ly_symbol2scm ("name"), g->get_property ("meta"));
106   return (scm_is_pair (name_pair)
107           ? ly_camel_case_2_lisp_identifier (scm_cdr (name_pair))
108           : SCM_EOL);
109 }
110
111 void
112 Metronome_mark_engraver::acknowledge_grob (Grob_info info)
113 {
114   Grob *g = info.grob ();
115
116   if (text_
117       && safe_is_member (grob_name_scm (g),
118                          text_->get_property ("non-break-align-symbols")))
119     text_->set_parent (g, X_AXIS);
120 }
121
122 void
123 Metronome_mark_engraver::stop_translation_timestep ()
124 {
125   if (text_)
126     {
127       if (text_->get_parent (X_AXIS)
128           && text_->get_parent (X_AXIS)->internal_has_interface (ly_symbol2scm ("multi-measure-rest-interface"))
129           && bar_)
130         text_->set_parent (bar_, X_AXIS);
131       else if (!support_)
132         {
133           /*
134             Gardner Read "Music Notation", p.278
135
136             Align the metronome mark over the time signature (or the
137             first notational element of the measure if no time
138             signature is present in that measure).
139           */
140           if (Grob *mc = unsmob_grob (get_property ("currentMusicalColumn")))
141             text_->set_parent (mc, X_AXIS);
142           else if (Grob *cc = unsmob_grob (get_property ("currentCommandColumn")))
143             text_->set_parent (cc, X_AXIS);
144         }
145       text_->set_object ("side-support-elements",
146                          grob_list_to_grob_array (get_property ("stavesFound")));
147       text_ = 0;
148       support_ = 0;
149       bar_ = 0;
150     }
151 }
152
153 void
154 Metronome_mark_engraver::process_music ()
155 {
156   SCM count = get_property ("tempoUnitCount");
157   SCM duration = get_property ("tempoUnitDuration");
158   SCM text = get_property ("tempoText");
159
160   if ( ( (unsmob_duration (duration) && scm_is_number (count))
161         || Text_interface::is_markup (text) )
162       && !(ly_is_equal (count, last_count_)
163            && ly_is_equal (duration, last_duration_)
164            && ly_is_equal (text, last_text_)))
165     {
166       text_ = make_item ("MetronomeMark", SCM_EOL);
167
168       SCM proc = get_property ("metronomeMarkFormatter");
169       SCM result = scm_call_4 (proc,
170                                text,
171                                duration,
172                                count,
173                                context ()->self_scm ());
174
175       text_->set_property ("text", result);
176     }
177
178   last_duration_ = duration;
179   last_count_ = count;
180   last_text_ = text;
181 }
182
183
184
185 ADD_ACKNOWLEDGER (Metronome_mark_engraver, break_aligned);
186 ADD_ACKNOWLEDGER (Metronome_mark_engraver, grob);
187
188 ADD_TRANSLATOR (Metronome_mark_engraver,
189                 /* doc */
190                 "Engrave metronome marking.  This delegates the formatting"
191                 " work to the function in the @code{metronomeMarkFormatter}"
192                 " property.  The mark is put over all staves.  The staves are"
193                 " taken from the @code{stavesFound} property, which is"
194                 " maintained by @ref{Staff_collecting_engraver}.",
195
196                 /* create */
197                 "MetronomeMark ",
198
199                 /* read */
200                 "stavesFound "
201                 "metronomeMarkFormatter "
202                 "tempoUnitDuration "
203                 "tempoUnitCount "
204                 "tempoText "
205                 "tempoHideNote ",
206
207                 /* write */
208                 ""
209                 );