]> git.donarmstrong.com Git - lilypond.git/blob - lily/cluster-engraver.cc
a9bd545314797a19250c787e68f401c8d4b67a70
[lilypond.git] / lily / cluster-engraver.cc
1 /*
2   cluster-engraver.cc -- implement Cluster_engraver
3
4   (c) 2002 Juergen Reuter <reuter@ipd.uka.de>
5 */
6
7 #include "engraver.hh"
8 #include "item.hh"
9 #include "spanner.hh"
10 #include "note-head.hh"
11 #include "warn.hh"
12
13 class Cluster_engraver : public Engraver
14 {
15
16 protected:
17 TRANSLATOR_DECLARATIONS(Cluster_engraver);
18   virtual void start_translation_timestep ();
19   virtual bool try_music (Music *);
20   virtual void process_music ();  
21   virtual void acknowledge_grob (Grob_info);
22   virtual void stop_translation_timestep ();
23
24 private:
25   Drul_array<Music*> reqs_drul_;
26   Pitch pitch_min, pitch_max;
27   Spanner *cluster;
28   SCM columns_scm;
29 };
30
31 void reset_min_max (Pitch *pitch_min, Pitch *pitch_max)
32 {
33   /*
34    * (pitch_min > pitch_max) means that pitches are not yet
35    * initialized
36    */
37   *pitch_min = Pitch (0, 0, +1);
38   *pitch_max = Pitch (0, 0, -1);
39 }
40
41 Cluster_engraver::Cluster_engraver ()
42 {
43   cluster = 0;
44   columns_scm = SCM_EOL;
45   reqs_drul_[LEFT] = reqs_drul_[RIGHT] = 0;
46 }
47
48 bool
49 Cluster_engraver::try_music (Music *m)
50 {
51   if (m->is_mus_type ("abort-event"))
52     {
53       reqs_drul_[START] = 0;
54       reqs_drul_[STOP] = 0;
55       if (cluster)
56         {
57           cluster->suicide ();
58           cluster = 0;
59           columns_scm = SCM_EOL;
60         }
61     }
62   else if (m->is_mus_type ("cluster-event"))
63     {
64       Direction d = to_dir (m->get_mus_property ("span-direction"));
65       reqs_drul_[d] = m;
66       return true;
67     }
68   return false;
69 }
70
71 void
72 Cluster_engraver::process_music ()
73 {
74   if (reqs_drul_[STOP])
75     {
76       if (!cluster)
77         {
78           reqs_drul_[STOP]->origin ()->warning ("can't find start of cluster");
79         }
80       else
81         {
82           Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
83           cluster->set_bound (RIGHT, bound);
84           cluster->set_grob_property ("segments", columns_scm);
85           typeset_grob (cluster);
86           cluster = 0;
87           columns_scm = SCM_EOL;
88         }
89       reqs_drul_[STOP] = 0;
90     }
91   if (reqs_drul_[START])
92     {
93       if (cluster)
94         {
95           reqs_drul_[START]->origin ()->warning ("may not nest clusters");
96         }
97       else
98         {
99           SCM basicProperties = get_property ("Cluster");
100           cluster = new Spanner (basicProperties);
101           columns_scm = SCM_EOL;
102           Grob *bound = unsmob_grob (get_property ("currentMusicalColumn"));
103           cluster->set_bound (LEFT, bound);
104           announce_grob (cluster, bound->self_scm ());
105         }
106       reqs_drul_[START] = 0;
107     }
108 }
109
110 void
111 Cluster_engraver::start_translation_timestep ()
112 {
113   reset_min_max (&pitch_min, &pitch_max);
114 }
115
116 void
117 Cluster_engraver::stop_translation_timestep ()
118 {
119   if (cluster)
120     {
121       SCM column_scm = get_property ("currentMusicalColumn");
122       if (column_scm == SCM_EOL)
123         {
124           programming_error("failed retrieving current column");
125           return;
126         }
127
128       if (Pitch::compare (pitch_min, pitch_max) <= 0)
129         {
130           int staff_position = pitch_min.steps ();
131           SCM c0 = get_property ("centralCPosition");
132           if (gh_number_p (c0))
133             staff_position += gh_scm2int (c0);
134           SCM segment = scm_list_n (column_scm,
135                                     gh_int2scm (staff_position),
136                                     pitch_min.smobbed_copy (),
137                                     pitch_max.smobbed_copy (),
138                                     SCM_UNDEFINED);
139           segment = scm_list_n (segment, SCM_UNDEFINED);
140           columns_scm = (columns_scm != SCM_EOL) ?
141             gh_append2 (columns_scm, segment) : segment;
142         }
143       else
144         {
145           /* This timestep is caused by a different voice of the same
146              staff and hence should be ignored. */
147         }
148     }
149 }
150
151 void
152 Cluster_engraver::acknowledge_grob (Grob_info info)
153 {
154   Item *item = dynamic_cast <Item *>(info.grob_);
155   if (item)
156     {
157       if (Note_head::has_interface (info.grob_))
158         {
159           Music *nr = info.music_cause ();
160           if (nr && nr->is_mus_type ("note-event"))
161             {
162               Pitch pitch = *unsmob_pitch (nr->get_mus_property ("pitch"));
163               if (Pitch::compare (pitch_min, pitch_max) > 0) // already init'd?
164                 {
165                   // not yet init'd; use current pitch to init min/max
166                   pitch_min = pitch;
167                   pitch_max = pitch;
168                 }
169               else if (Pitch::compare (pitch, pitch_max) > 0) // new max?
170                 {
171                   pitch_max = pitch;
172                 }
173               else if (Pitch::compare (pitch, pitch_min) < 0) // new min?
174                 {
175                   pitch_min = pitch;
176                 }
177             }
178         }
179     }
180 }
181
182 ENTER_DESCRIPTION(Cluster_engraver,
183 /* descr */     "engraves a cluster",
184 /* creats*/     "Cluster",
185 /* accepts */   "cluster-event",
186 /* acks  */     "note-head-interface",
187 /* reads */     "",
188 /* write */     "");