]> git.donarmstrong.com Git - lilypond.git/blob - lily/chord-name-engraver.cc
release: 1.1.24
[lilypond.git] / lily / chord-name-engraver.cc
1 /*
2   chord-name-engraver.cc -- implement Chord_name_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include "chord-name-engraver.hh"
10 #include "musical-request.hh"
11 #include "paper-def.hh"
12 #include "lookup.hh"
13 #include "paper-def.hh"
14 #include "main.hh"
15 #include "dimensions.hh"
16 #include "g-text-item.hh"
17
18 ADD_THIS_TRANSLATOR (Chord_name_engraver);
19
20 Chord_name_engraver::Chord_name_engraver ()
21 {
22 }
23
24 void
25 Chord_name_engraver::acknowledge_element (Score_element_info i)
26 {
27   if (Note_req* n = dynamic_cast<Note_req*> (i.req_l_))
28     pitch_arr_.push (n->pitch_);
29 }
30
31 bool
32 Chord_name_engraver::do_try_music (Music* m)
33 {
34   if (Note_req* n = dynamic_cast<Note_req*> (m))
35     {
36       pitch_arr_.push (n->pitch_);
37       return true;
38     }
39   return false;
40 }
41
42   /*
43     find tonic: after longest line of triads
44    */
45 int
46 Chord_name_engraver::find_tonic_i () const
47 {
48   int tonic_i = 0;
49   int longest_i = 0;
50   for (int i = 0; i < pitch_arr_.size (); i++)
51     for (int j = 0; j < pitch_arr_.size (); j++)
52       {
53         int gap = pitch_arr_[(i + j + 1) % pitch_arr_.size ()].notename_i_
54           - pitch_arr_[(i + j) % pitch_arr_.size ()].notename_i_;
55         while (gap < 0)
56           gap += 7;
57         gap %= 7;
58         if (gap != 2)
59           {
60             if (j > longest_i)
61               {
62                 longest_i = j;
63                 tonic_i = i;
64               }
65             break;
66           }
67       }
68
69   int biggest_i = 0;
70   if (!longest_i)
71     for (int i = 0; i < pitch_arr_.size (); i++)
72       {
73         int gap = pitch_arr_[i].notename_i_
74           - pitch_arr_[(i - 1 + pitch_arr_.size ()) 
75           % pitch_arr_.size ()].notename_i_;
76         while (gap < 0)
77           gap += 7;
78         gap %= 7;
79         if (gap > biggest_i)
80           {
81             biggest_i = gap;
82             tonic_i = i;
83           }
84       }
85   return tonic_i;
86 }
87
88 Array<Musical_pitch>
89 Chord_name_engraver::rebuild_pitch_arr (int tonic_i) const
90 {
91   Musical_pitch last (0, 0, -5);
92   Array<Musical_pitch> pitches;
93   for (int i = 0; i < pitch_arr_.size (); i++)
94     {
95       Musical_pitch p = pitch_arr_[(tonic_i + i) % pitch_arr_.size ()];
96       if (p < last)
97         {
98           p.octave_i_ = last.octave_i_;
99           if (p < last)
100             p.octave_i_++;
101         }
102       pitches.push (p);
103       last = p;
104     }
105   return pitches;
106 }
107
108 void
109 Chord_name_engraver::do_process_requests ()
110 {
111   if (text_p_arr_.size ())
112     return;
113   if (!pitch_arr_.size ())
114     return;
115
116   /*
117    Banter style chord names (almost).
118    TODO:
119      - move this stuff to new Item class Chord_name
120      - switch on property, add american (?) chordNameStyle
121
122   Scalar chordNameStyle = get_property ("chordNameStyle", 0);
123   if (chordNameStyle == "Banter")
124      chord = pitches_to_banter (pitch_arr_));
125
126    */
127
128   int tonic_i = 0;
129   Musical_pitch inversion = pitch_arr_[0];
130   Scalar chord_inversions = get_property ("chordInversion", 0);
131   if (chord_inversions.to_bool ())
132     {
133       tonic_i = find_tonic_i ();
134       if (tonic_i)
135         pitch_arr_ = rebuild_pitch_arr (tonic_i);
136     }
137     
138
139   G_text_item* item_p =  new G_text_item;
140
141   item_p->text_str_ = banter_str (pitch_arr_, tonic_i, inversion);
142   
143   Scalar style = get_property ("textstyle", 0);
144   if (style.length_i ())
145     item_p->style_str_ = style;
146   
147   text_p_arr_.push (item_p);
148   announce_element (Score_element_info (item_p, 0));
149 }
150
151 void
152 Chord_name_engraver::do_pre_move_processing ()
153 {
154   for (int i=0; i < text_p_arr_.size (); i++)
155     {
156       typeset_element (text_p_arr_[i]);
157     }
158   text_p_arr_.clear ();
159   pitch_arr_.clear ();
160 }
161
162 String
163 Chord_name_engraver::banter_str (Array<Musical_pitch> pitch_arr, int tonic_i, Musical_pitch inversion) const
164 {
165   Musical_pitch tonic = pitch_arr[0];
166
167   Array<Musical_pitch> scale;
168   scale.push (Musical_pitch (0)); // c
169   scale.push (Musical_pitch (1)); // d
170   scale.push (Musical_pitch (2)); // e
171   scale.push (Musical_pitch (3)); // f
172   scale.push (Musical_pitch (4)); // g
173   scale.push (Musical_pitch (5)); // a
174   // 7 always means 7-...
175   scale.push (Musical_pitch (6, -1)); // b
176
177
178   for (int i = 0; i < scale.size (); i++)
179     scale[i].transpose (tonic);
180
181   //urg, should do translation in scheme.
182   char const *acc[] = {"\\textflat\\textflat ", "\\textflat ", "", "\\textsharp " , "\\textsharp\\textsharp "};
183   String tonic_str = tonic.str ();
184   tonic_str = tonic_str.left_str (1).upper_str ()
185     + acc[tonic.accidental_i_ + 2];
186
187   String add_str;
188   String sub_str;
189   String sep_str;
190   int last_trap = 1;
191   for (int i=1; i < pitch_arr.size (); i++)
192     {
193       Musical_pitch p = pitch_arr[i];
194       int trap = p.notename_i_ - tonic.notename_i_
195         + (p.octave_i_ - tonic.octave_i_) * 7 + 1;
196       while (trap - last_trap > 2)
197         {
198           last_trap += 2;
199           sub_str += sep_str + "no" + to_str (last_trap);
200           sep_str = "/";
201         }
202       last_trap = trap;
203       int accidental = p.accidental_i_ - scale[(trap - 1) % 7].accidental_i_;
204       if ((trap == 3) && (accidental == -1))
205         tonic_str += "m"; // hmm
206       else if (accidental || (!(trap % 2) || ((i + 1 == pitch_arr.size ()) && (trap > 5))))
207         {
208           add_str += sep_str;
209           if ((trap == 7) && (accidental == 1))
210             add_str += "maj7";
211           else
212             {
213               add_str += to_str (trap);
214               if (accidental)
215                 add_str += accidental < 0 ? "-" : "+";
216             }
217           sep_str = "/";
218         }
219     }
220
221   String inversion_str;
222   if (tonic_i)
223     {
224       inversion_str = inversion.str ();
225       inversion_str = "/" + inversion_str.left_str (1).upper_str ()
226         + acc[tonic.accidental_i_ + 2];
227
228     }
229
230   String str = tonic_str + "$^{" + add_str + sub_str + "}$" + inversion_str;
231   return str;
232 }