]> git.donarmstrong.com Git - lilypond.git/blob - lily/ambitus.cc
* lily/melisma-engraver.cc (try_music): use melisma_busy()
[lilypond.git] / lily / ambitus.cc
1 /*
2   ambitus.cc -- implement Ambitus
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "staff-symbol-referencer.hh"
10 #include "pitch.hh"
11 #include "ambitus.hh"
12 #include "molecule.hh"
13 #include "note-head.hh"
14 #include "item.hh"
15 #include "font-interface.hh"
16 #include "paper-def.hh"
17 #include "lookup.hh"
18
19 /*
20  * TODO: note-head collision handling
21  *
22  * TODO: accidentals collision handling
23  *
24  * TODO: alternative representation: adding the ambitus as text script
25  * to the instrument name (e.g. "Soprano (c^1 - f^2)").
26  *
27  * FIXME: Accidentals are too close at the note heads (it seems that
28  * the extent of the ledger lines is ignored).
29  *
30  * TODO: If (depending on breakAlignOrder) ambitus is put behind
31  * key-signature, then do not repeat accidentals that already appear
32  * in the key signature.
33  *
34  * FIXME: A staff containing more than a single context will result in
35  * multiple ambitus grobs per staff.  This is basically ok, but there is
36  * currently no proper collision handling for this case.
37  *
38  * TODO: make ignore_octave and force_accidental of function
39  * number_accidentals accessible via grob properties.
40  */
41
42 /**
43   Given a pitch and a key_signature, decide what accidentals to show.
44  
45   Possible return values:
46  
47   0: do not show any accidental
48   1: show pitch->alteration_ only
49   2: show pitch->get_alteration, preceded by a natural sign
50
51   UGH: code duplication! See accidental-engraver.
52  
53  */
54 static int
55 number_accidentals (SCM key_signature, Pitch *pitch,
56                     bool ignore_octave_b, bool force_accidental)
57 {
58   int notename = pitch->get_notename ();
59   int octave = pitch->get_octave();
60   int alteration = pitch->get_alteration();
61
62   if (force_accidental) // ignore key signature
63     return 1;
64
65   
66 #if DEBUG_AMBITUS
67   scm_display (key_signature, scm_current_output_port ());
68 #endif
69
70   SCM prev;
71   if (ignore_octave_b)
72     prev = ly_assoc_cdr (scm_int2num (notename), key_signature);
73   else
74     prev = scm_assoc (gh_cons (scm_int2num (octave), scm_int2num (notename)),
75                      key_signature);
76
77   /* should really be true unless prev == SCM_BOOL_F */
78   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
79     {
80       prev = gh_cons (ly_car (prev), ly_cadr (prev));
81     }
82
83   /* If an accidental was not found */
84   if (prev == SCM_BOOL_F)
85     prev = scm_assoc (scm_int2num (notename), key_signature);
86
87   SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
88   int sig_alteration = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
89
90   if (alteration == sig_alteration) // no accidental at all needed
91     return 0;
92
93   if ((alteration == 0) && (sig_alteration != 0)) // need ordinary natural
94     return 2;
95
96   if (sig_alteration == 0) // use pitch's alteration
97     return 1;
98
99   return 2;
100 }
101
102 void
103 add_accidentals (Item *me, Molecule *head, int num_acc,
104                  Pitch *pitch, String accidentals_style, Real yoffs)
105 {
106   if (!num_acc)
107     return;
108   if (pitch->get_alteration())
109     {
110       Molecule accidental (Font_interface::get_default_font (me)->
111                            find_by_name (String ("accidentals-") +
112                                          accidentals_style +
113                                          to_string (pitch->get_alteration ())));
114       accidental.translate_axis (yoffs, Y_AXIS);
115       head->add_at_edge (X_AXIS,  LEFT, accidental, 0.1, 0);
116     }
117   if (num_acc == 2)
118     {
119       Molecule natural (Font_interface::get_default_font (me)->
120                         find_by_name (String ("accidentals-") +
121                                       accidentals_style +
122                                       to_string ("0")));
123       natural.translate_axis (yoffs, Y_AXIS);
124       head->add_at_edge (X_AXIS,  LEFT, natural, 0.1, 0);
125     }
126 }
127
128 MAKE_SCHEME_CALLBACK (Ambitus,brew_molecule,1);
129 SCM
130 Ambitus::brew_molecule (SCM smob)
131 {
132   Item *me = (Item *)unsmob_grob (smob);
133   Molecule molecule = Molecule ();
134
135   SCM scm_note_head_style = me->get_grob_property ("note-head-style");
136   String note_head_style;
137   if (gh_symbol_p (scm_note_head_style))
138     {
139       String note_head_style =
140         ly_symbol2string (scm_note_head_style);
141     }
142   else
143     {
144       note_head_style = String ("noteheads-2");
145     }
146   if (Font_interface::get_default_font (me)->find_by_name (note_head_style).is_empty ())
147     {
148       String message = "Ambitus: no such note head: `" + note_head_style + "'";
149       me->warning (_ (message.to_str0 ()));
150       return SCM_EOL;
151     }
152
153   /*
154     FIXME: Use positions. 
155    */
156   int p_min, p_max;
157   Pitch *pitch_min = unsmob_pitch (me->get_grob_property ("pitch-min"));
158   if (!pitch_min)
159     {
160       me->programming_error("Ambitus: pitch_min undefined; assuming 0");
161       p_min = 0;
162     }
163   else
164     {
165       p_min = pitch_min->steps ();
166     }
167   Pitch *pitch_max = unsmob_pitch (me->get_grob_property ("pitch-max"));
168   if (!pitch_max)
169     {
170       me->programming_error("Ambitus: pitch_max undefined; assuming 0");
171       p_max = 0;
172     }
173   else
174     {
175       p_max = pitch_max->steps ();
176     }
177   if (p_min > p_max)
178     {
179       me->programming_error ("Ambitus: reverse range");
180     }
181
182   SCM c0 = me->get_grob_property ("c0-position");
183   if (gh_number_p (c0))
184     {
185       p_min += gh_scm2int (c0);
186       p_max += gh_scm2int (c0);
187     }
188
189   // create heads
190   Molecule head_min =
191     Font_interface::get_default_font (me)->find_by_name (note_head_style);
192   head_min.translate_axis (0.5*p_min, Y_AXIS);
193   Molecule head_max =
194     Font_interface::get_default_font (me)->find_by_name (note_head_style);
195   head_max.translate_axis (0.5*p_max, Y_AXIS);
196
197   // join heads
198   if (to_boolean (me->get_grob_property ("join-heads")) &&
199       ((p_max - p_min) >= 3))
200     {
201       Real linethickness = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
202       Real blotdiameter = me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
203       Interval x_extent = 0.5 * Interval (-linethickness, +linethickness);
204       Interval y_extent = 0.5 * Interval (p_min + 1.35, p_max - 1.35);
205       Box line_box (x_extent, y_extent);
206       Molecule line = Lookup::round_filled_box (line_box, blotdiameter);
207       line.translate_axis (0.5 * head_min.extent (X_AXIS).length (), X_AXIS);
208       molecule.add_molecule (line);
209     }
210
211   // add ledger lines
212   Interval hd = head_min.extent (X_AXIS);
213   Real left_ledger_protusion = hd.length () / 4;
214   Real right_ledger_protusion = left_ledger_protusion;
215   Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
216                                  hd[RIGHT] + right_ledger_protusion);
217   Molecule ledger_lines;
218   int interspaces = Staff_symbol_referencer::line_count (me) - 1;
219   ledger_lines =
220     Note_head::brew_ledger_lines (me, p_min, interspaces, l_extents, true);
221   ledger_lines.translate_axis (0.5 * p_min, Y_AXIS);
222   molecule.add_molecule (ledger_lines);
223   ledger_lines =
224     Note_head::brew_ledger_lines (me, p_max, interspaces, l_extents, true);
225   ledger_lines.translate_axis (0.5 * p_max, Y_AXIS);
226   molecule.add_molecule (ledger_lines);
227
228   // add accidentals
229   SCM key_signature = me->get_grob_property ("key-signature");
230   SCM scm_accidentals_style = me->get_grob_property ("accidentals-style");
231   String accidentals_style;
232   if (gh_symbol_p (scm_accidentals_style))
233     {
234       accidentals_style =
235         ly_symbol2string (scm_accidentals_style);
236     }
237   else
238     {
239       accidentals_style = String ("");
240     }
241   int num_acc;
242   num_acc = number_accidentals (key_signature, pitch_min, true, false);
243   add_accidentals (me, &head_min, num_acc, pitch_min,
244                    accidentals_style, 0.5 * p_min);
245   num_acc = number_accidentals (key_signature, pitch_max, true, false);
246   add_accidentals (me, &head_max, num_acc, pitch_max,
247                    accidentals_style, 0.5 * p_max);
248
249   // add heads
250   molecule.add_molecule (head_min);
251   molecule.add_molecule (head_max);
252
253   return molecule.smobbed_copy ();
254 }
255
256 ADD_INTERFACE (Ambitus, "ambitus-interface",
257   "An ambitus represents the pitch range of a voice.",
258   "c0-position pitch-min pitch-max accidentals note-head-style join-heads");