]> git.donarmstrong.com Git - lilypond.git/blob - lily/lyric-engraver.cc
Merge branch 'master' of /home/jcharles/GIT/Lily/. into translation
[lilypond.git] / lily / lyric-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
5   Jan Nieuwenhuizen <janneke@gnu.org>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "context.hh"
22 #include "engraver.hh"
23 #include "item.hh"
24 #include "note-head.hh"
25 #include "stream-event.hh"
26 #include "international.hh"
27
28 #include "translator.icc"
29
30 /**
31    Generate texts for lyric syllables.  We only do one lyric at a time.
32    Multiple copies of this engraver should be used to do multiple voices.
33 */
34 class Lyric_engraver : public Engraver
35 {
36 protected:
37   void stop_translation_timestep ();
38   void process_music ();
39   void listen_lyric (Stream_event *);
40
41 public:
42   TRANSLATOR_DECLARATIONS (Lyric_engraver);
43
44 private:
45   Stream_event *event_;
46   Item *text_;
47   Item *last_text_;
48
49   Context *get_voice_context ();
50 };
51
52 Lyric_engraver::Lyric_engraver ()
53 {
54   text_ = 0;
55   last_text_ = 0;
56   event_ = 0;
57 }
58
59 void
60 Lyric_engraver::listen_lyric (Stream_event *ev)
61 {
62   ASSIGN_EVENT_ONCE (event_, ev);
63 }
64
65 void
66 Lyric_engraver::process_music ()
67 {
68   if (event_)
69     {
70       SCM text = event_->get_property ("text");
71
72       if (ly_is_equal (text, scm_from_ascii_string (" ")))
73         {
74           if (last_text_)
75             last_text_->set_property ("self-alignment-X",
76                                       get_property ("lyricMelismaAlignment"));
77         }
78       else
79         text_ = make_item ("LyricText", event_->self_scm ());
80     }
81
82   Context *voice = get_voice_to_lyrics (context ());
83   if (last_text_
84       && voice
85       && to_boolean (voice->get_property ("melismaBusy"))
86       && !to_boolean (context ()->get_property ("ignoreMelismata")))
87     last_text_->set_property ("self-alignment-X",
88                               get_property ("lyricMelismaAlignment"));
89 }
90
91 Context *
92 get_voice_to_lyrics (Context *lyrics)
93 {
94   bool searchForVoice = to_boolean (lyrics->get_property ("searchForVoice"));
95
96   SCM avc = lyrics->get_property ("associatedVoiceContext");
97   if (Context *c = unsmob<Context> (avc))
98     {
99       if (!c->is_removable ())
100         return c;
101     }
102
103   SCM voice_name = lyrics->get_property ("associatedVoice");
104   string nm = lyrics->id_string ();
105
106   if (scm_is_string (voice_name))
107     nm = ly_scm2string (voice_name);
108   else if (nm == "" || !searchForVoice)
109     return 0;
110   else
111     {
112       ssize idx = nm.rfind ('-');
113       if (idx != NPOS)
114         nm = nm.substr (0, idx);
115     }
116
117   SCM voice_type = lyrics->get_property ("associatedVoiceType");
118   if (!scm_is_symbol (voice_type))
119     return 0;
120
121   Context *voice = find_context_near (lyrics, voice_type, nm);
122   if (voice)
123     return voice;
124
125   return find_context_near (lyrics, voice_type, "");
126 }
127
128 Grob *
129 get_current_note_head (Context *voice)
130 {
131   Moment now = voice->now_mom ();
132   for (SCM s = voice->get_property ("busyGrobs");
133        scm_is_pair (s); s = scm_cdr (s))
134     {
135       Grob *g = unsmob<Grob> (scm_cdar (s));;
136       Moment *end_mom = unsmob<Moment> (scm_caar (s));
137       if (!end_mom || !g)
138         {
139           programming_error ("busyGrobs invalid");
140           continue;
141         }
142
143       // It's a bit irritating that we just have the length and
144       // duration of the Grob.
145       Moment end_from_now =
146         get_event_length (unsmob<Stream_event> (g->get_property ("cause")), now)
147         + now;
148       // We cannot actually include more than a single grace note
149       // using busyGrobs on ungraced lyrics since a grob ending on
150       // grace time will just have disappeared from busyGrobs by the
151       // time our ungraced lyrics appear.  At best we may catch a
152       // single grace note.
153       //
154       // However, a single grace note ending on a non-grace time is
155       // indistinguishable from a proper note ending on a non-grace
156       // time.  So we really have no way to obey includeGraceNotes
157       // here.  Not with this mechanism.
158       if ((*end_mom == end_from_now)
159           && dynamic_cast<Item *> (g)
160           && has_interface<Note_head> (g))
161         {
162           return g;
163         }
164     }
165
166   return 0;
167 }
168
169 void
170 Lyric_engraver::stop_translation_timestep ()
171 {
172   if (text_)
173     {
174       Context *voice = get_voice_to_lyrics (context ());
175
176       if (voice)
177         {
178           Grob *head = get_current_note_head (voice);
179
180           if (head)
181             {
182               text_->set_parent (head->get_parent(X_AXIS), X_AXIS);
183               if (melisma_busy (voice)
184                   && !to_boolean (get_property ("ignoreMelismata")))
185                 text_->set_property ("self-alignment-X",
186                                      get_property ("lyricMelismaAlignment"));
187             }
188         }
189
190       last_text_ = text_;
191       text_ = 0;
192     }
193   event_ = 0;
194 }
195
196 void
197 Lyric_engraver::boot ()
198 {
199   ADD_LISTENER (Lyric_engraver, lyric);
200 }
201
202 ADD_TRANSLATOR (Lyric_engraver,
203                 /* doc */
204                 "Engrave text for lyrics.",
205
206                 /* create */
207                 "LyricText ",
208
209                 /* read */
210                 "ignoreMelismata "
211                 "lyricMelismaAlignment "
212                 "searchForVoice",
213
214                 /* write */
215                 ""
216                );