]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-quanting.cc
Merge branch 'master' into lilypond/translation
[lilypond.git] / lily / beam-quanting.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5   Jan Nieuwenhuizen <janneke@gnu.org>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "beam-scoring-problem.hh"
22
23 #include <queue>  
24 #include <algorithm>
25 using namespace std;
26
27 #include "align-interface.hh"
28 #include "beam.hh"
29 #include "grob.hh"
30 #include "international.hh"
31 #include "main.hh"
32 #include "output-def.hh"
33 #include "pointer-group-interface.hh"
34 #include "staff-symbol-referencer.hh"
35 #include "stem.hh"
36 #include "warn.hh"
37
38 Real
39 get_detail (SCM alist, SCM sym, Real def)
40 {
41   SCM entry = scm_assq (sym, alist);
42
43   if (scm_is_pair (entry))
44     return robust_scm2double (scm_cdr (entry), def);
45   return def;
46 }
47
48 void
49 Beam_quant_parameters::fill (Grob *him)
50 {
51   SCM details = him->get_property ("details");
52
53   SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
54   STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
55   REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
56   BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
57   STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
58   DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
59   HINT_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("hint-direction-penalty"), 20);
60   MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
61   IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
62   ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
63 }
64
65 static Real
66 shrink_extra_weight (Real x, Real fac)
67 {
68   return fabs (x) * ((x < 0) ? fac : 1.0);
69 }
70
71 /****************************************************************/
72
73 Beam_configuration::Beam_configuration ()
74 {
75   y = Interval (0.0, 0.0);
76   demerits = 0.0;
77   next_scorer_todo = ORIGINAL_DISTANCE;
78 }
79
80 bool Beam_configuration::done () const
81 {
82   return next_scorer_todo >= NUM_SCORERS;
83 }
84
85 void Beam_configuration::add (Real demerit, const string &reason)
86 {
87   demerits += demerit;
88
89 #if DEBUG_BEAM_SCORING
90   if (demerit) 
91     score_card_ += to_string (" %s %.2f", reason.c_str (), demerit);
92 #endif
93 }
94   
95 Beam_configuration* Beam_configuration::new_config (Interval start,
96                                                     Interval offset)
97 {
98   Beam_configuration* qs = new Beam_configuration;
99   qs->y = Interval (int (start[LEFT]) + offset[LEFT],
100                     int (start[RIGHT]) + offset[RIGHT]);
101
102   // This orders the sequence so we try combinations closest to the
103   // the ideal offset first.
104   Real start_score = abs (offset[RIGHT]) + abs (offset[LEFT]);
105   qs->demerits = start_score / 1000.0;
106   qs->next_scorer_todo = ORIGINAL_DISTANCE + 1;
107   
108   return qs;
109 }
110
111 /****************************************************************/
112
113 /*
114   TODO:
115
116   - Make all demerits customisable
117
118   - One sensible check per demerit (what's this --hwn)
119
120   - Add demerits for quants per se, as to forbid a specific quant
121   entirely
122 */
123 int
124 best_quant_score_idx (vector<Beam_configuration*> const &configs)
125 {
126   Real best = 1e6;
127   int best_idx = -1;
128   for (vsize i = configs.size (); i--;)
129     {
130       if (configs[i]->demerits < best)
131         {
132           best = configs [i]->demerits;
133           best_idx = i;
134         }
135     }
136
137   return best_idx;
138 }
139
140 // This is a temporary hack to see how much we can gain by using a
141 // priority queue on the beams to score.
142 static int score_count = 0;
143 LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0,
144            (),
145            "count number of beam scores.") {
146   return scm_from_int (score_count);
147 }
148
149 void Beam_scoring_problem::init_stems ()
150 {
151   extract_grob_set (beam, "stems", stems);
152   for (int a = 2; a--;)
153     common[a] = common_refpoint_of_array (stems, beam, Axis (a));
154
155   Grob *fvs = Beam::first_normal_stem (beam);
156   Grob *lvs = Beam::last_normal_stem (beam);
157     
158   x_span = Interval (fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0,
159                      lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0);
160
161   Drul_array<bool> dirs_found (0, 0);
162   for (vsize i = 0; i < stems.size (); i++)
163     {
164       Grob *s = stems[i];
165       if (!Stem::is_normal_stem (s))
166         continue;
167       
168       Stem_info si (Stem::get_stem_info (s));
169       si.scale (1 / staff_space);
170       stem_infos.push_back (si);
171       dirs_found[si.dir_] = true;
172
173       bool f = to_boolean (s->get_property ("french-beaming"))
174         && s != lvs && s != fvs;
175
176       Real y = Beam::calc_stem_y (beam, s, common, x_span[LEFT], x_span[RIGHT], CENTER, 
177                                   Interval (0, 0), f);
178       base_lengths.push_back (y / staff_space);
179       stem_xpositions.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS));
180     }
181   
182   edge_dirs = Drul_array<Direction> (CENTER, CENTER);
183   if (stem_infos.size ())
184     {
185       edge_dirs = Drul_array<Direction> (stem_infos[0].dir_,
186                                          stem_infos.back().dir_);
187     }
188
189   is_xstaff = Align_interface::has_interface (common[Y_AXIS]);
190   is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
191   
192   staff_radius = Staff_symbol_referencer::staff_radius (beam);
193   edge_beam_counts =  Drul_array<int>
194     (Stem::beam_multiplicity (stems[0]).length () + 1,
195      Stem::beam_multiplicity (stems.back ()).length () + 1);
196     
197   beam_translation = Beam::get_beam_translation (beam) / staff_space;
198 }
199
200 Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array<Real> ys)
201 {
202   beam = me;
203   unquanted_y = ys;
204     
205   /*
206     Calculations are relative to a unit-scaled staff, i.e. the quants are
207     divided by the current staff_space.
208   */
209   staff_space = Staff_symbol_referencer::staff_space (me);
210   beam_thickness = Beam::get_beam_thickness (me) / staff_space;
211   line_thickness = Staff_symbol_referencer::line_thickness (me) / staff_space;
212
213   // This is the least-squares DY, corrected for concave beams.
214   musical_dy = robust_scm2double (me->get_property ("least-squares-dy"), 0);
215
216   parameters.fill (me);
217   init_stems ();
218 }
219
220 void
221 Beam_scoring_problem::generate_quants (vector<Beam_configuration*> *scores) const
222 {
223   int region_size = (int) parameters.REGION_SIZE;
224
225   /*
226     Knees are harder, lets try some more possibilities for knees.
227   */  
228   if (is_knee)
229     region_size += 2;
230   
231   Real straddle = 0.0;
232   Real sit = (beam_thickness - line_thickness) / 2;
233   Real inter = 0.5;
234   Real hang = 1.0 - (beam_thickness - line_thickness) / 2;
235   Real base_quants [] = {straddle, sit, inter, hang};
236   int num_base_quants = int (sizeof (base_quants) / sizeof (Real));
237
238   /*
239     Asymetry ? should run to <= region_size ?
240   */
241   vector<Real> unshifted_quants;
242   for (int i = -region_size; i < region_size; i++)
243     for (int j = 0; j < num_base_quants; j++)
244       {
245         unshifted_quants.push_back (i + base_quants[j]);
246       }
247
248   for (vsize i = 0; i < unshifted_quants.size (); i++)
249     for (vsize j = 0; j < unshifted_quants.size (); j++)
250       scores->push_back (Beam_configuration::new_config (unquanted_y,
251                                                          Interval (unshifted_quants[i],
252                                                                    unshifted_quants[j])));
253 }
254
255
256 void Beam_scoring_problem::one_scorer (Beam_configuration* config) const
257 {
258   score_count ++;
259   switch (config->next_scorer_todo) {
260   case SLOPES:
261     score_slopes_dy (config);
262     break;
263   case FORBIDDEN:
264     score_forbidden_quants (config);
265     break;
266   case STEM_LENGTHS:
267     score_stem_lengths (config);
268     break;
269    
270   case NUM_SCORERS:
271   case ORIGINAL_DISTANCE:
272   default:
273     assert (false);
274   }
275   config->next_scorer_todo++;
276 }                                  
277
278
279 Beam_configuration *
280 Beam_scoring_problem::force_score (SCM inspect_quants, const vector<Beam_configuration*> &configs) const
281 {
282   Drul_array<Real> ins = ly_scm2interval (inspect_quants);
283   Real mindist = 1e6;
284   Beam_configuration *best = NULL; 
285   for (vsize i = 0; i < configs.size (); i++)
286     {
287       Real d = fabs (configs[i]->y[LEFT]- ins[LEFT]) + fabs (configs[i]->y[RIGHT] - ins[RIGHT]);
288       if (d < mindist)
289         {
290           best = configs[i];
291           mindist = d;
292         }
293     }
294   if (mindist > 1e5)
295     programming_error ("cannot find quant");
296
297   return best;
298 }
299
300 Drul_array<Real>
301 Beam_scoring_problem::solve () const {
302   vector<Beam_configuration*> configs;
303   generate_quants (&configs);
304
305   Beam_configuration *best = NULL;  
306
307   SCM inspect_quants = beam->get_property ("inspect-quants");
308   if (to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))
309       && scm_is_pair (inspect_quants))
310     {
311       best = force_score (inspect_quants, configs);
312     }
313   else
314     {
315       std::priority_queue<Beam_configuration*, std::vector<Beam_configuration*>,
316                           Beam_configuration_less> queue;
317       for (vsize i = 0; i < configs.size(); i++)
318         queue.push(configs[i]);
319
320
321       /*
322         TODO
323
324         It would be neat if we generated new configurations on the
325         fly, depending on the best complete score so far, eg.
326
327         if (best->done()) {
328           if (best->demerits < sqrt(queue.size())
329             break;
330           while (best->demerits > sqrt(queue.size()) {
331             generate and insert new configuration
332           }
333         }
334
335         that would allow us to do away with region_size altogether.
336       */
337       while (true) {
338         best = queue.top ();
339         if (best->done ())
340           break;
341
342         queue.pop ();
343         one_scorer (best);
344         queue.push (best);
345       }
346     }
347
348   Interval final_positions = best->y;
349
350 #if DEBUG_BEAM_SCORING
351   if (to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))))
352     {
353       // debug quanting
354       int completed = 0;
355       for (vsize i = 0; i < configs.size (); i++)
356         {
357           if (configs[i]->done ())
358             completed++;
359         }
360
361       string card = best->score_card_ + to_string (" c%d/%d", completed, configs.size());
362       beam->set_property ("quant-score", ly_string2scm (card));
363     }
364 #endif
365
366   junk_pointers (configs);
367   return final_positions;
368 }
369
370 void
371 Beam_scoring_problem::score_stem_lengths (Beam_configuration* config) const
372 {
373   Real limit_penalty = parameters.STEM_LENGTH_LIMIT_PENALTY;
374   Drul_array<Real> score (0, 0);
375   Drul_array<int> count (0, 0);
376
377   for (vsize i = 0; i < stem_xpositions.size (); i++)
378     {
379       Real x = stem_xpositions[i];
380       Real dx = x_span.delta ();
381       Real beam_y = dx
382         ? config->y[RIGHT] * (x - x_span[LEFT]) / dx + config->y[LEFT] * (x_span[RIGHT] - x) / dx
383         : (config->y[RIGHT] + config->y[LEFT]) / 2;
384       Real current_y = beam_y + base_lengths[i];
385       Real length_pen = parameters.STEM_LENGTH_DEMERIT_FACTOR;
386
387       Stem_info info = stem_infos[i];
388       Direction d = info.dir_;
389
390       score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
391
392       Real ideal_diff = d * (current_y - info.ideal_y_);
393       Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
394
395       /* We introduce a power, to make the scoring strictly
396          convex. Otherwise a symmetric knee beam (up/down/up/down)
397          does not have an optimum in the middle. */
398       if (is_knee)
399         ideal_score = pow (ideal_score, 1.1);
400
401       score[d] += length_pen * ideal_score;
402       count[d]++;
403     }
404
405   /* Divide by number of stems, to make the measure scale-free. */
406   Direction d = DOWN;
407   do
408     score[d] /= max (count[d], 1);
409   while (flip (&d) != DOWN);
410
411   config->add (score[LEFT] + score[RIGHT], "L");
412 }
413
414 void
415 Beam_scoring_problem::score_slopes_dy (Beam_configuration *config) const
416 {
417   Real dy = config->y.delta ();
418   Real damped_dy = unquanted_y.delta();
419   Real dem = 0.0;
420   
421   /*
422     DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
423     complex beaming patterns, horizontal is often a good choice.
424
425     TODO: find a way to incorporate the complexity of the beam in this
426     penalty.
427   */
428   if (sign (damped_dy) != sign (dy))
429     {
430       if (!dy)
431         {
432           if (fabs (damped_dy / x_span.delta ()) > parameters.ROUND_TO_ZERO_SLOPE)
433             dem += parameters.DAMPING_DIRECTION_PENALTY;
434           else
435             dem += parameters.HINT_DIRECTION_PENALTY;
436         }
437       else
438         dem += parameters.DAMPING_DIRECTION_PENALTY;
439     }
440   
441   dem += parameters.MUSICAL_DIRECTION_FACTOR
442     * max (0.0, (fabs (dy) - fabs (musical_dy)));
443
444   Real slope_penalty = parameters.IDEAL_SLOPE_FACTOR;
445
446   /* Xstaff beams tend to use extreme slopes to get short stems. We
447      put in a penalty here. */
448   if (is_xstaff)
449     slope_penalty *= 10;
450
451   /* Huh, why would a too steep beam be better than a too flat one ? */
452   dem += shrink_extra_weight (fabs (damped_dy) - fabs (dy), 1.5)
453     * slope_penalty;
454
455   config->add (dem, "S");
456 }
457
458 static Real
459 my_modf (Real x)
460 {
461   return x - floor (x);
462 }
463
464 /*
465   TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
466   because for 32nd and 64th beams the forbidden quants are relatively
467   more important than stem lengths.
468 */
469 void
470 Beam_scoring_problem::score_forbidden_quants (Beam_configuration *config) const
471 {
472   Real dy = config->y.delta ();
473
474   Real extra_demerit = parameters.SECONDARY_BEAM_DEMERIT /
475     max (edge_beam_counts[LEFT], edge_beam_counts[RIGHT]);
476
477   Direction d = LEFT;
478   Real dem = 0.0;
479   Real eps = parameters.BEAM_EPS;
480
481   do
482     {
483       for (int j = 1; j <= edge_beam_counts[d]; j++)
484         {
485           Direction stem_dir = edge_dirs[d];
486
487           /*
488             The 2.2 factor is to provide a little leniency for
489             borderline cases. If we do 2.0, then the upper outer line
490             will be in the gap of the (2, sit) quant, leading to a
491             false demerit.
492           */
493           Real gap1 = config->y[d] - stem_dir * ((j - 1) * beam_translation + beam_thickness / 2 - line_thickness / 2.2);
494           Real gap2 = config->y[d] - stem_dir * (j * beam_translation - beam_thickness / 2 + line_thickness / 2.2);
495
496           Interval gap;
497           gap.add_point (gap1);
498           gap.add_point (gap2);
499
500           for (Real k = -staff_radius;
501                k <= staff_radius + eps; k += 1.0)
502             if (gap.contains (k))
503               {
504                 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
505
506                 /*
507                   this parameter is tuned to grace-stem-length.ly
508                 */
509                 Real fixed_demerit = 0.4;
510
511                 dem += extra_demerit
512                   * (fixed_demerit
513                      + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
514               }
515         }
516     }
517   while ((flip (&d)) != LEFT);
518
519   if (max (edge_beam_counts[LEFT], edge_beam_counts[RIGHT]) >= 2)
520     {
521       Real straddle = 0.0;
522       Real sit = (beam_thickness - line_thickness) / 2;
523       Real inter = 0.5;
524       Real hang = 1.0 - (beam_thickness - line_thickness) / 2;
525
526       Direction d = LEFT;
527       do
528         {
529           if (edge_beam_counts[d] >= 2
530               && fabs (config->y[d] - edge_dirs[d] * beam_translation) < staff_radius + inter)
531             {
532               // TODO up/down symmetry.
533               if (edge_dirs[d] == UP && dy <= eps
534                   && fabs (my_modf (config->y[d]) - sit) < eps)
535                 dem += extra_demerit;
536
537               if (edge_dirs[d] == DOWN && dy >= eps
538                   && fabs (my_modf (config->y[d]) - hang) < eps)
539                 dem += extra_demerit;
540             }
541
542           if (edge_beam_counts[d] >= 3
543               && fabs (config->y[d] - 2 * edge_dirs[d] * beam_translation) < staff_radius + inter)
544             {
545               // TODO up/down symmetry.
546               if (edge_dirs[d] == UP && dy <= eps
547                   && fabs (my_modf (config->y[d]) - straddle) < eps)
548                 dem += extra_demerit;
549
550               if (edge_dirs[d] == DOWN && dy >= eps
551                   && fabs (my_modf (config->y[d]) - straddle) < eps)
552                 dem += extra_demerit;
553             }
554         }
555       while (flip (&d) != LEFT);
556     }
557
558   config->add (dem, "F");
559 }
560