]> git.donarmstrong.com Git - lilypond.git/blob - lily/ambitus.cc
* input/test/ambitus.ly:
[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 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_i_ only
49  * 2: show pitch->alteration_i_, preceded by a natural sign
50  */
51 static int
52 number_accidentals (SCM key_signature, Pitch *pitch,
53                     bool ignore_octave_b, bool force_accidental)
54 {
55   int notename = pitch->notename_i_;
56   int octave = pitch->octave_i_;
57   int alteration = pitch->alteration_i_;
58
59   if (force_accidental) // ignore key signature
60     return 1;
61
62 #if DEBUG_AMBITUS
63   scm_display (key_signature, scm_current_output_port ());
64 #endif
65
66   SCM prev;
67   if (ignore_octave_b)
68     prev = ly_assoc_cdr (gh_int2scm (notename), key_signature);
69   else
70     prev = gh_assoc (gh_cons (gh_int2scm (octave), gh_int2scm (notename)),
71                      key_signature);
72
73   /* should really be true unless prev == SCM_BOOL_F */
74   if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
75     {
76       prev = gh_cons (ly_car (prev), ly_cadr (prev));
77     }
78
79   /* If an accidental was not found */
80   if (prev == SCM_BOOL_F)
81     prev = gh_assoc (gh_int2scm (notename), key_signature);
82
83   SCM prev_acc = (prev == SCM_BOOL_F) ? gh_int2scm (0) : ly_cdr (prev);
84   int sig_alteration = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
85
86   if (alteration == sig_alteration) // no accidental at all needed
87     return 0;
88
89   if ((alteration == 0) && (sig_alteration != 0)) // need ordinary natural
90     return 2;
91
92   if (sig_alteration == 0) // use pitch's alteration
93     return 1;
94
95   return 2;
96 }
97
98 void
99 add_accidentals (Item *me, Molecule *head, int num_acc,
100                  Pitch *pitch, String accidentals_style, Real yoffs)
101 {
102   if (!num_acc)
103     return;
104   if (pitch->alteration_i_)
105     {
106       Molecule accidental (Font_interface::get_default_font (me)->
107                            find_by_name (String ("accidentals-") +
108                                          accidentals_style +
109                                          to_str (pitch->alteration_i_)));
110       accidental.translate_axis (yoffs, Y_AXIS);
111       head->add_at_edge (X_AXIS,  LEFT, accidental, 0.1);
112     }
113   if (num_acc == 2)
114     {
115       Molecule natural (Font_interface::get_default_font (me)->
116                         find_by_name (String ("accidentals-") +
117                                       accidentals_style +
118                                       to_str ("0")));
119       natural.translate_axis (yoffs, Y_AXIS);
120       head->add_at_edge (X_AXIS,  LEFT, natural, 0.1);
121     }
122 }
123
124 MAKE_SCHEME_CALLBACK (Ambitus,brew_molecule,1);
125 SCM
126 Ambitus::brew_molecule (SCM smob)
127 {
128   Item *me = (Item *)unsmob_grob (smob);
129   Molecule molecule = Molecule ();
130
131   SCM scm_note_head_style = me->get_grob_property ("note-head-style");
132   String note_head_style;
133   if (gh_symbol_p (scm_note_head_style))
134     {
135       String note_head_style =
136         ly_scm2string (scm_symbol_to_string (scm_note_head_style));
137     }
138   else
139     {
140       note_head_style = String ("noteheads-2");
141     }
142   if (Font_interface::get_default_font (me)->find_by_name (note_head_style).empty_b ())
143     {
144       String message = "Ambitus: no such note head: `" + note_head_style + "'";
145       me->warning (_ (message.ch_C ()));
146       return SCM_EOL;
147     }
148
149   Pitch *pitch_min = unsmob_pitch (me->get_grob_property ("pitch-min"));
150   int p_min = pitch_min->steps ();
151   Pitch *pitch_max = unsmob_pitch (me->get_grob_property ("pitch-max"));
152   int p_max = pitch_max->steps ();
153   if (p_min > p_max)
154     {
155       String message = "Ambitus: no range to output";
156       me->warning (_ (message.ch_C ()));
157       return SCM_EOL;
158     }
159
160   SCM c0 = me->get_grob_property ("centralCPosition");
161   if (gh_number_p (c0))
162     {
163       p_min += gh_scm2int (c0);
164       p_max += gh_scm2int (c0);
165     }
166
167   // create heads
168   Molecule head_min =
169     Font_interface::get_default_font (me)->find_by_name (note_head_style);
170   head_min.translate_axis (0.5*p_min, Y_AXIS);
171   Molecule head_max =
172     Font_interface::get_default_font (me)->find_by_name (note_head_style);
173   head_max.translate_axis (0.5*p_max, Y_AXIS);
174
175   // join heads
176   if (to_boolean (me->get_grob_property ("join-heads")) &&
177       ((p_max - p_min) >= 3))
178     {
179       Real linethickness = me->paper_l ()->get_var ("linethickness");
180       Real blotdiameter = me->paper_l ()->get_var ("blotdiameter");
181       Interval x_extent = 0.5 * Interval (-linethickness, +linethickness);
182       Interval y_extent = 0.5 * Interval (p_min + 1.35, p_max - 1.35);
183       Box line_box (x_extent, y_extent);
184       Molecule line = Lookup::roundfilledbox (line_box, blotdiameter);
185       line.translate_axis (0.5 * head_min.extent (X_AXIS).length (), X_AXIS);
186       molecule.add_molecule (line);
187     }
188
189   // add ledger lines
190   Interval hd = head_min.extent (X_AXIS);
191   Real left_ledger_protusion = hd.length () / 4;
192   Real right_ledger_protusion = left_ledger_protusion;
193   Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
194                                  hd[RIGHT] + right_ledger_protusion);
195   Molecule ledger_lines;
196   int interspaces = Staff_symbol_referencer::line_count (me) - 1;
197   ledger_lines =
198     Note_head::brew_ledger_lines (me, p_min, interspaces, l_extents, true);
199   ledger_lines.translate_axis (0.5 * p_min, Y_AXIS);
200   molecule.add_molecule (ledger_lines);
201   ledger_lines =
202     Note_head::brew_ledger_lines (me, p_max, interspaces, l_extents, true);
203   ledger_lines.translate_axis (0.5 * p_max, Y_AXIS);
204   molecule.add_molecule (ledger_lines);
205
206   // add accidentals
207   SCM key_signature = me->get_grob_property ("keySignature");
208   SCM scm_accidentals_style = me->get_grob_property ("accidentals-style");
209   String accidentals_style;
210   if (gh_symbol_p (scm_accidentals_style))
211     {
212       accidentals_style =
213         ly_scm2string (scm_symbol_to_string (scm_accidentals_style));
214     }
215   else
216     {
217       accidentals_style = String ("");
218     }
219   int num_acc;
220   num_acc = number_accidentals (key_signature, pitch_min, true, false);
221   add_accidentals (me, &head_min, num_acc, pitch_min,
222                    accidentals_style, 0.5 * p_min);
223   num_acc = number_accidentals (key_signature, pitch_max, true, false);
224   add_accidentals (me, &head_max, num_acc, pitch_max,
225                    accidentals_style, 0.5 * p_max);
226
227   // add heads
228   molecule.add_molecule (head_min);
229   molecule.add_molecule (head_max);
230
231   return molecule.smobbed_copy ();
232 }
233
234 ADD_INTERFACE (Ambitus, "ambitus-interface",
235   "An ambitus represents the pitch range of a voice.",
236   "note-head-style join-heads");