]> git.donarmstrong.com Git - lilypond.git/blob - lily/chord-name.cc
patch::: 1.3.45.jcn2
[lilypond.git] / lily / chord-name.cc
1 /*
2   chord-name.cc -- implement Chord_name
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1999--2000 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "chord-name.hh"
10 #include "musical-request.hh"
11 #include "warn.hh"
12 #include "debug.hh"
13 #include "molecule.hh"
14 #include "paper-def.hh"
15 #include "lookup.hh"
16 #include "staff-symbol-referencer.hh"
17
18
19 /*
20   TODO: move text lookup out of Chord_name
21  */
22
23 /*
24   word is roman text or styled text:
25    "text"
26    ("style" . "text")
27  */
28 Molecule
29 Chord_name::ly_word2molecule (SCM scm) const
30 {
31   String style;
32   if (gh_pair_p (scm))
33     {
34       SCM s = gh_car (scm);
35       if (gh_string_p (s))
36         style = ly_scm2string (s);
37       scm = gh_cdr (scm);
38     }
39   if (gh_string_p (scm))
40     return lookup_l ()->text (style, ly_scm2string (scm), paper_l ());
41   return Molecule ();
42 }
43
44 /*
45  scm is word or list of words:
46    word
47    (word word)
48  */
49 Molecule
50 Chord_name::ly_text2molecule (SCM scm) const
51 {
52   Molecule mol;
53   if (gh_list_p (scm))
54     {
55       while (gh_cdr (scm) != SCM_EOL)
56         {
57           Molecule m = ly_word2molecule (gh_car (scm));
58           if (!m.empty_b ())
59             mol.add_at_edge (X_AXIS, RIGHT, m, 0);
60           scm = gh_cdr (scm);
61         }
62       scm = gh_car (scm);
63     }  
64   Molecule m = ly_word2molecule (scm);
65   if (!m.empty_b ())
66     mol.add_at_edge (X_AXIS, RIGHT, m, 0);
67   return mol;
68 }
69
70 Molecule
71 Chord_name::pitch2molecule (Musical_pitch p) const
72 {
73   SCM name = scm_eval (gh_list (ly_symbol2scm ("user-pitch-name"),
74                                 ly_quote_scm (p.to_scm ()),
75                                 SCM_UNDEFINED));
76
77   if (name != SCM_UNSPECIFIED)
78     {
79       return ly_text2molecule (name);
80     }
81
82   Molecule mol = lookup_l ()->text ("", p.str ().left_str (1).upper_str (), paper_l ());
83
84   /*
85     We want the smaller size, even if we're big ourselves.
86    */
87   if (p.accidental_i_)
88     {
89       Molecule acc = paper_l ()->lookup_l (-3)->afm_find
90         (String ("accidentals-") + to_str (p.accidental_i_));
91       // urg, howto get a good superscript_y?
92       Real super_y = lookup_l ()->text ("", "x", paper_l ()).extent
93         ()[Y_AXIS].length () / 2;
94       super_y += -acc.extent ()[Y_AXIS][MIN];
95       acc.translate_axis (super_y, Y_AXIS);
96       mol.add_at_edge (X_AXIS, RIGHT, acc, 0.0);
97     }
98                      
99   return mol;
100 }
101
102 Musical_pitch
103 diff_pitch (Musical_pitch tonic, Musical_pitch  p)
104 {
105   Musical_pitch diff (p.notename_i_ - tonic.notename_i_, 
106     p.accidental_i_ - tonic.accidental_i_, 
107     p.octave_i_ - tonic.octave_i_);
108
109   while  (diff.notename_i_ >= 7)
110     {
111       diff.notename_i_ -= 7;
112       diff.octave_i_ ++;
113     }
114   while  (diff.notename_i_ < 0)
115     {
116       diff.notename_i_ += 7;
117       diff.octave_i_ --;
118     }
119
120   diff.accidental_i_ -= (tonic.semitone_pitch () + diff.semitone_pitch ())
121     - p.semitone_pitch ();
122
123   return diff;
124 }
125
126 /*
127   JUNKME
128  */
129 bool
130 Chord_name::user_chord_name (Array<Musical_pitch> pitch_arr, Chord_mol* name_p) const
131 {
132   Array<Musical_pitch> chord_type = pitch_arr;
133   Chord::rebuild_transpose (&chord_type, diff_pitch (pitch_arr[0], Musical_pitch (0)), false);
134
135   SCM chord = SCM_EOL;
136   for (int i= chord_type.size (); i--; )
137     chord = gh_cons (chord_type[i].to_scm (), chord);
138
139
140   SCM name = scm_eval (gh_list (ly_symbol2scm ("user-chord-name"),
141                                 ly_quote_scm (chord),
142                                 SCM_UNDEFINED));
143   if (gh_pair_p (name))
144     {
145       name_p->modifier_mol = ly_text2molecule (gh_car (name));
146       name_p->addition_mol = ly_text2molecule (gh_cdr (name));
147       return true;
148     }
149   return false;
150 }
151
152 void
153 Chord_name::banter (Array<Musical_pitch> pitch_arr, Chord_mol* name_p) const
154 {
155   Array<Musical_pitch> add_arr;
156   Array<Musical_pitch> sub_arr;
157   Chord::find_additions_and_subtractions (pitch_arr, &add_arr, &sub_arr);
158                            
159   Array<Musical_pitch> scale;
160   for (int i=0; i < 7; i++)
161     scale.push (Musical_pitch (i));
162
163   Musical_pitch tonic = pitch_arr[0];
164   Chord::rebuild_transpose (&scale, tonic, true);
165   
166   /*
167     Does chord include this step?  -1 if flat
168    */
169   int has[16];
170   for (int i=0; i<16; i++)
171     has[i] = 0;
172
173   String mod_str;
174   String add_str;
175   String sep_str;
176   for (int i = 0; i < add_arr.size (); i++)
177     {
178       Musical_pitch p = add_arr[i];
179       int step = Chord::step_i (tonic, p);
180       int accidental = p.accidental_i_ - scale[(step - 1) % 7].accidental_i_;
181       if ((step < 16) && (has[step] != -1))
182         has[step] = accidental == -1 ? -1 : 1;
183       // only from guile table ?
184       if ((step == 3) && (accidental == -1))
185         {
186           mod_str = "m";
187         }
188       else if (accidental
189                || (!(step % 2) 
190                || ((i == add_arr.size () - 1) && (step > 5))))
191         {
192           add_str += sep_str;
193           sep_str = "/";
194           if ((step == 7) && (accidental == 1))
195             {
196               add_str += "maj7";
197             }
198           else
199             {
200               add_str += to_str (step);
201               if (accidental)
202                 add_str += accidental < 0 ? "-" : "+";
203             }
204         }
205     }
206
207   for (int i = 0; i < sub_arr.size (); i++)
208     {
209       Musical_pitch p = sub_arr[i];
210       int step = Chord::step_i (tonic, p);
211       /*
212         if additions include 2 or 4, assume sus2/4 and don't display 'no3'
213       */
214       if (!((step == 3) && (has[2] || has[4])))
215         {
216           add_str += sep_str + "no" + to_str (step);
217           sep_str = "/";
218         }
219     }
220
221   if (mod_str.length_i ())
222     name_p->modifier_mol.add_at_edge (X_AXIS, RIGHT, 
223       lookup_l ()->text ("roman", mod_str, paper_l ()), 0);
224   if (add_str.length_i ())
225     {
226       if (!name_p->addition_mol.empty_b ())
227         add_str = "/" + add_str;
228       name_p->addition_mol.add_at_edge (X_AXIS, RIGHT,
229        lookup_l ()->text ("script", add_str, paper_l ()), 0);
230     }
231 }
232
233 /*
234   TODO:
235     fix silly to-and-fro scm conversions
236  */
237 Molecule 
238 Chord_name::do_brew_molecule () const
239 {
240   Array<Musical_pitch> pitch_arr;
241   
242   for (SCM s = get_elt_property ("pitches"); s != SCM_EOL; s = gh_cdr (s))
243     pitch_arr.push (Musical_pitch (gh_car (s)));
244   
245   Musical_pitch tonic = pitch_arr[0];
246   
247   Chord_mol name;
248   name.tonic_mol = pitch2molecule (tonic);
249
250   /*
251     if user has explicitely listed chord name, use that
252     
253     TODO
254     urg
255     maybe we should check all sub-lists of pitches, not
256     just full list and base triad?
257    */
258   if (!user_chord_name (pitch_arr, &name))
259     {
260       /*
261         else, check if user has listed base triad
262         use user base name and add banter for remaining part
263        */
264       if ((pitch_arr.size () > 2)
265           && user_chord_name (pitch_arr.slice (0, 3), &name))
266         {
267           Array<Musical_pitch> base = Chord::base_arr (tonic);
268           base.concat (pitch_arr.slice (3, pitch_arr.size ()));
269           banter (base, &name);
270         }
271       /*
272         else, use pure banter
273        */
274       else
275         {
276           banter (pitch_arr, &name);
277         }
278     }
279
280   SCM s = get_elt_property ("inversion");
281   if (s != SCM_UNDEFINED)
282     {
283       name.inversion_mol = lookup_l ()->text ("", "/", paper_l ());
284       Musical_pitch p (s);
285
286       Molecule mol = pitch2molecule (p);
287       name.inversion_mol.add_at_edge (X_AXIS, RIGHT, mol, 0);
288     }
289
290   s = get_elt_property ("bass");
291   if (s != SCM_UNDEFINED)
292     {
293       name.bass_mol = lookup_l ()->text ("", "/", paper_l ());
294       Musical_pitch p (s);
295       Molecule mol = pitch2molecule (p);
296       name.bass_mol.add_at_edge (X_AXIS, RIGHT, mol, 0);
297     }
298
299   // urg, howto get a good superscript_y?
300   Real super_y = lookup_l ()->text ("", "x", paper_l ()).extent
301     ()[Y_AXIS].length () / 2;
302   if (!name.addition_mol.empty_b ())
303     name.addition_mol.translate (Offset (0, super_y));
304
305   Molecule  mol;
306   mol.add_at_edge (X_AXIS, RIGHT, name.tonic_mol, 0);
307   // huh?
308   if (!name.modifier_mol.empty_b ())
309     mol.add_at_edge (X_AXIS, RIGHT, name.modifier_mol, 0);
310   if (!name.addition_mol.empty_b ())
311     mol.add_at_edge (X_AXIS, RIGHT, name.addition_mol, 0);
312   if (!name.inversion_mol.empty_b ())
313     mol.add_at_edge (X_AXIS, RIGHT, name.inversion_mol, 0);
314   if (!name.bass_mol.empty_b ())
315     mol.add_at_edge (X_AXIS, RIGHT, name.bass_mol, 0);
316
317   s = get_elt_property ("word-space");
318   if (gh_number_p (s))
319     mol.dim_.interval_a_[X_AXIS][RIGHT] += gh_scm2double (s)
320       * staff_symbol_referencer (this).staff_space ();
321
322   return mol;
323 }