diff -Naur lilypond-1.5.44/ChangeLog lilypond-1.5.44.NEW/ChangeLog --- lilypond-1.5.44/ChangeLog Wed Mar 20 02:10:21 2002 +++ lilypond-1.5.44.NEW/ChangeLog Thu Mar 21 03:31:07 2002 @@ -1,3 +1,7 @@ +2002-03-21 Juergen Reuter + + * lily/rest-engraver.cc: mensural style vertical aligment of rests + 2002-03-19 Han-Wen Nienhuys * VERSION: 1.5.44 released diff -Naur lilypond-1.5.44/lily/rest-engraver.cc lilypond-1.5.44.NEW/lily/rest-engraver.cc --- lilypond-1.5.44/lily/rest-engraver.cc Wed Feb 27 16:13:27 2002 +++ lilypond-1.5.44.NEW/lily/rest-engraver.cc Wed Mar 20 03:59:01 2002 @@ -1,27 +1,61 @@ /* - rest-grav.cc -- implement Rest_engraver + rest-engraver.cc -- implement Rest_engraver source file of the GNU LilyPond music typesetter (c) 1997--2002 Han-Wen Nienhuys */ #include "item.hh" +#include "note-head.hh" #include "staff-symbol-referencer.hh" #include "musical-request.hh" #include "dots.hh" #include "rhythmic-head.hh" #include "engraver.hh" +#include "warn.hh" +/* + * TODO: add maxima rest to feta/parmesan font. + * + * TODO: add mensural outside staff rests to feta/parmesan font. + * (BTW: Why not compose outside staff rests from ordinary rests plus + * ledger lines?) + * + * TODO: enhance maxima alignment support -- these are currently + * simply aligned in a straight row. + * + * TODO: find a solution for the mensural_valign () issue (see TODOs + * below in the code for details) + * + * TODO: should fix stop_translation_timestep () anomaly (see below) */ class Rest_engraver : public Engraver { Rest_req *rest_req_l_; - Item * dot_p_; - Grob* rest_p_; + Grob *rest_p_; + Link_array pending_rests_arr_; + Link_array pending_dots_arr_; + bool timestep_has_note; + + /* + * For each sequence of rests, watch the pitches range of the note + * column that preceeds and follows the sequence. Use this info for + * vertical placement decision of sequence of rests inbetween. + */ + SCM last_pitch_max, current_pitch_max; + SCM last_pitch_min, current_pitch_min; + Moment current_pitch_moment; + + void Rest_engraver::mensural_valign (Grob *rest_p_, Grob *dot_p_, bool reset, + int pre_pitch, int post_pitch); + void Rest_engraver::mensural_valign (); + bool Rest_engraver::last_timestep (); + protected: virtual bool try_music (Music *); virtual void stop_translation_timestep (); virtual void start_translation_timestep (); + virtual void acknowledge_grob (Grob_info); virtual void process_music (); public: @@ -34,30 +68,329 @@ */ Rest_engraver::Rest_engraver () { - rest_req_l_ =0; - rest_p_ =0; - dot_p_ =0; + rest_req_l_ = 0; + rest_p_ = 0; + last_pitch_max = SCM_EOL; + last_pitch_min = SCM_EOL; + current_pitch_max = SCM_EOL; + current_pitch_min = SCM_EOL; + current_pitch_moment = Moment (0); + + /* + * TODO: As soon as the stop bug is fixed, remove the following + * assigment which serves as work-around only. The stop bug as of + * lily 1.5.34 reveals on Moment 0 by invoking + * stop_translation_timestep () without a preceeding call to + * start_translation_timestep (). + */ + timestep_has_note = false; } void Rest_engraver::start_translation_timestep () { - rest_req_l_ =0; + rest_req_l_ = 0; + timestep_has_note = false; } void -Rest_engraver::stop_translation_timestep () +Rest_engraver::mensural_valign (Grob *rest_p_, Grob *dot_p_, bool reset, + int pre_pitch, int post_pitch) { - if (rest_p_) + // preserve longa status information across subsequent invocations: + static Direction longa_align_dir; + static int longa_align_elongation; + static int longa_count; + int line_count; + + /* + * Here we actually would like to call + * "Staff_symbol_referencer::line_count (rest_p_)", but at this + * point, there is no Staff_symbol associated to rest_p_ yet, and, + * consequently, this function would return 0. Hence, we have to + * figure out the number of lines manually by directly searching for + * the proper SCM symbol. + */ + SCM line_count_scm = scm_sloppy_assq (ly_symbol2scm ("line-count"), + get_property ("StaffSymbol")); + line_count = + (line_count_scm == SCM_BOOL_F) ? 0 : gh_scm2int (ly_cdr (line_count_scm)); + + int longa_max_pos = max (0, line_count - 3); + int longa_min_pos = - max (0, line_count - 3); + + SCM c0_scm = get_property ("centralCPosition"); + int c0 = 0; + if (gh_number_p (c0_scm)) + { + c0 = gh_scm2int (c0_scm); + } + + bool even_lc = (line_count % 2) == 0; // even number of staff lines? + + int pre_pos = c0 + pre_pitch; + if (pre_pos & 1) pre_pos--; // align with lower staff line + pre_pos |= even_lc; // correction if even number of staff lines + + int post_pos = c0 + post_pitch; + if (post_pos & 1) post_pos--; // dto. + post_pos |= even_lc; + + int avg_pos = c0 + (pre_pitch + post_pitch) / 2; + if (avg_pos & 1) avg_pos--; // dto. + avg_pos |= even_lc; + + SCM durlog_scm = rest_p_->get_grob_property ("duration-log"); + int durlog; + if (gh_number_p (durlog_scm)) + { + durlog = gh_scm2int (durlog_scm); + } + else + { + programming_error (_ ("bad rest duration log; assuming 0")); + durlog = 0; + } + + if (reset) // init status for starting new sequence of arbitrary rests + { + longa_count = -1; // mark longa sequence as uninitialized + } + + if ((durlog == -2) && (longa_count == -1)) // start sequence of longa rests + { + longa_count = 0; // longa sequence started + longa_align_elongation = pre_pos; // start aligning with pre_pitch + + // clip to fit in staff + pre_pos = min(pre_pos, longa_max_pos); + pre_pos = max(pre_pos, longa_min_pos); + + + // align sequence towards center of staff + longa_align_dir = (Direction)sign (-pre_pos); + + if (longa_align_dir == CENTER) + { + // we are already at the center of the staff (neutral case); + // pragmatically decide for aligning upwards + longa_align_dir = UP; + } + } + + int pos; // the final vertical position of the current rest + + if (durlog == -2) // handle longa + { + int longa_cluster_size = + gh_scm2int (rest_p_->get_grob_property ("longa-cluster-size")); + if ((longa_cluster_size != 0) && // clustering turned on + (longa_count >= longa_cluster_size) && // cluster full + (longa_max_pos > longa_min_pos) // need space to move longa rests + ) // next cluster + { + if (((longa_align_dir == UP) && + (longa_align_elongation >= longa_max_pos)) || + ((longa_align_dir == DOWN) && + (longa_align_elongation <= longa_min_pos))) + // next longa would be out of maximal elongation; hence + // reverse alignment direction + { + longa_align_dir = -longa_align_dir; + } + + // move 2 steps into current direction to align with + // next staff line + longa_align_elongation += 2*longa_align_dir; + + longa_count = 0; + } + longa_count++; + pos = longa_align_elongation; + } + else { - typeset_grob (rest_p_); - rest_p_ =0; + longa_count = -1; // not a longa -> stop any running longa sequence + + // for all rests other than longa use the average position + pos = avg_pos; } + + Staff_symbol_referencer::set_position (rest_p_, pos); if (dot_p_) { - typeset_grob (dot_p_); - dot_p_ =0; - } + Staff_symbol_referencer::set_position (dot_p_, pos); + } +} + +void +Rest_engraver::mensural_valign () +{ + SCM c0_scm = get_property ("centralCPosition"); + int c0 = 0; + if (gh_number_p (c0_scm)) + { + c0 = gh_scm2int (c0_scm); + } + + SCM pre_pitch_max = last_pitch_max; + SCM pre_pitch_min = last_pitch_min; + SCM post_pitch_max = current_pitch_max; + SCM post_pitch_min = current_pitch_min; + + int pre_pitch_max_steps, post_pitch_max_steps; + int pre_pitch_min_steps, post_pitch_min_steps; + + if (pre_pitch_max == SCM_EOL) + { + // Pitch (-c0, 0, 0) will center the rest on the staff + pre_pitch_max_steps = -c0; + pre_pitch_min_steps = -c0; + } + else + { + pre_pitch_max_steps = unsmob_pitch (pre_pitch_max)->steps (); + pre_pitch_min_steps = unsmob_pitch (pre_pitch_min)->steps (); + } + if (post_pitch_max == SCM_EOL) + { + // Pitch (-c0, 0, 0) will center the rest on the staff + post_pitch_max_steps = -c0; + post_pitch_min_steps = -c0; + } + else + { + post_pitch_max_steps = unsmob_pitch (post_pitch_max)->steps (); + post_pitch_min_steps = unsmob_pitch (post_pitch_min)->steps (); + } + + int pre_pitch_steps_avg = (pre_pitch_max_steps + pre_pitch_min_steps) / 2; + int post_pitch_steps_avg = (post_pitch_max_steps + post_pitch_min_steps) / 2; + + /* + * TODO: In the below code, the for loop header should be replaced + * with the following line as soon as the mensural_valign () issue + * (see below) has been solved. + * + * for (int i = pending_rests_arr_.size (); i--;) + */ + for (int i = pending_rests_arr_.size () - 1; i >= 0; i = -1) + { + Grob *rest_p_ = pending_rests_arr_[i]; + Grob *dot_p_ = pending_rests_arr_[i]; + mensural_valign (rest_p_, dot_p_, i == 0, + pre_pitch_steps_avg, post_pitch_steps_avg); + } +} + +bool +Rest_engraver::last_timestep () +{ + // TODO + return false; +} + +void +Rest_engraver::stop_translation_timestep () +{ + if ((pending_rests_arr_.size () > 0) && + (timestep_has_note || last_timestep ())) + { + /* + * TODO: Actually we really should call mensural_valign () here, + * but it seems this is too late for showing + * Staff_symbol_referencer::set_position (rest_p_, pos) any + * effect. As a workaround, mensural_valign () is already + * called in process_music (); see the comment over there for + * details. See also the modification in mensural_valign (). + * The following lines of code should be commented in as soon as + * this issue has been resolved. + * + * Grob *rest_p_ = pending_rests_arr_[0]; + * SCM alignment_style_scm = + * (rest_p_) ? rest_p_->get_grob_property ("alignment-style") : SCM_EOL; + * String alignment_style; + * if ((gh_symbol_p (alignment_style_scm)) && + * (aligmnet_style_scm != SCM_EOL)) + * { + * alignment_style = + * ly_scm2string (scm_symbol_to_string (alignment_style_scm)); + * } + * else + * { + * warning (_ ("rest alignment style undefined; using default")); + * alignment_style = "default"; + * } + * if (String::compare_i (alignment_style, "mensural") == 0) + * mensural_valign (); + */ + for (int i = pending_rests_arr_.size (); i--;) + { + typeset_grob (pending_rests_arr_[i]); + Grob *dot_p_ = pending_dots_arr_[i]; + if (dot_p_) + { + typeset_grob (dot_p_); + } + } + pending_rests_arr_.clear (); + pending_dots_arr_.clear (); + + /* + * A piece may end with a sequence of rests. Therefore, we must + * ensure that current_pitch is not set to an outdated value in + * the case that there are no more notes until the end of the + * piece. Hence, to be on the safe side, we clear it now. + */ + current_pitch_max = SCM_EOL; + current_pitch_min = SCM_EOL; + } + rest_p_ = 0; +} + +void +Rest_engraver::acknowledge_grob (Grob_info info) +{ + Item *item = dynamic_cast (info.grob_l_); + if (item) + { + if (Note_head::has_interface (info.grob_l_)) // watch surrounding pitches + { + timestep_has_note = true; + Note_req *nr = dynamic_cast (info.music_cause ()); + Moment moment = now_mom (); + if ((current_pitch_min == SCM_EOL) || + (current_pitch_max == SCM_EOL) || + (moment != current_pitch_moment)) // start a new pitch range + { + current_pitch_moment = moment; + current_pitch_min = nr->get_mus_property ("pitch"); + current_pitch_max = nr->get_mus_property ("pitch"); + if (pending_rests_arr_.size () == 0) + /* + * We are not about completing a sequence of rests, so + * save the current pitches as possible candidates for + * the latest pitches before a sequence of rests. + */ + { + last_pitch_max = current_pitch_max; + last_pitch_min = current_pitch_min; + } + } + else // (moment == current_pitch_moment) -> update current pitch range + { + SCM pitch = nr->get_mus_property("pitch"); + if (Pitch::less_p (pitch, current_pitch_min)) + { + current_pitch_min = pitch; + } + else if (Pitch::less_p (current_pitch_max, pitch)) + { + current_pitch_max = pitch; + } + } + } + } } void @@ -67,8 +400,8 @@ { rest_p_ = new Item (get_property ("Rest")); Rhythmic_head::set_interface (rest_p_); + Item *dot_p_ = 0; - int durlog = unsmob_duration (rest_req_l_->get_mus_property ("duration"))-> duration_log (); rest_p_->set_grob_property ("duration-log", @@ -85,7 +418,6 @@ dot_p_->set_grob_property ("dot-count", gh_int2scm (dots)); announce_grob (dot_p_, SCM_EOL); } - Pitch *p = unsmob_pitch (rest_req_l_->get_mus_property ("pitch")); /* @@ -98,11 +430,39 @@ SCM c0 = get_property ("centralCPosition"); if (gh_number_p (c0)) pos += gh_scm2int (c0); - + rest_p_->set_grob_property ("staff-position", gh_int2scm (pos)); } - + announce_grob(rest_p_, rest_req_l_->self_scm()); + pending_rests_arr_.push (rest_p_); + pending_dots_arr_.push (dot_p_); + + /* + * TODO: As a workaround, we currently call mensural_valign () + * already over here, although the first pitch after the + * sequence of rests is not yet known. It actually should be + * called in stop_translation_timestep () after the pitch is + * known; see the comment over there for details. See also the + * modification in mensural_valign (). The following lines of + * code should be removed as soon as this issue has been + * resolved. + */ + SCM alignment_style_scm = rest_p_->get_grob_property ("alignment-style"); + String alignment_style; + if ((gh_symbol_p (alignment_style_scm)) && + (alignment_style_scm != SCM_EOL)) + { + alignment_style = + ly_scm2string (scm_symbol_to_string (alignment_style_scm)); + } + else + { + warning (_ ("rest alignment style undefined; using default")); + alignment_style = "default"; + } + if (String::compare_i (alignment_style, "mensural") == 0) + mensural_valign (); } } @@ -122,6 +482,6 @@ ENTER_DESCRIPTION(Rest_engraver, /* descr */ "", /* creats*/ "Rest Dots", -/* acks */ "", +/* acks */ "note-head-interface", /* reads */ "centralCPosition", /* write */ ""); diff -Naur lilypond-1.5.44/scm/grob-description.scm lilypond-1.5.44.NEW/scm/grob-description.scm --- lilypond-1.5.44/scm/grob-description.scm Tue Mar 19 01:46:28 2002 +++ lilypond-1.5.44.NEW/scm/grob-description.scm Wed Mar 20 03:45:21 2002 @@ -594,6 +594,9 @@ (molecule-callback . ,Rest::brew_molecule) (Y-offset-callbacks . (,Staff_symbol_referencer::callback)) (minimum-beam-collision-distance . 1.5) + (style . default) + (alignment-style . default) + (longa-cluster-size . 1) (meta . ,(grob-description rhythmic-head-interface staff-symbol-referencer-interface diff -Naur lilypond-1.5.44/scm/grob-property-description.scm lilypond-1.5.44.NEW/scm/grob-property-description.scm --- lilypond-1.5.44/scm/grob-property-description.scm Tue Mar 19 00:34:00 2002 +++ lilypond-1.5.44.NEW/scm/grob-property-description.scm Wed Mar 20 03:49:37 2002 @@ -34,6 +34,7 @@ (grob-property-description 'adjust-if-on-staffline boolean? "If this grob is on a staff line, adjust its appearance, so that it better fits into the staff. E.g., if set true on stem grobs, flares of mensural flags will always be aligned with the staff lines, regardless if the associated note head is printed on a staff line or inbetween.") (grob-property-description 'after-line-breaking-callback procedure? "Procedure taking a grob as argument. This procedure is called (using dependency resolution) after line breaking. Return value is ignored.") +(grob-property-description 'alignment-style symbol? "tells an engraver, the rest-engraver) to align this grob according to rules of a certain notation style. Currently, this property is only supported on rest symbols; valid values are 'default and 'mensural.") (grob-property-description 'align number? "the alignment of the text, 0 is horizontal, 1 is vertical.") (grob-property-description 'align-dir dir? "Which side to align? -1: left side, 0: around center of width, 1: right side.") (grob-property-description 'alignment-done boolean? "boolean to administrate whether we've done the alignment already (to ensure that the process is done only once).") @@ -225,6 +226,7 @@ ") (grob-property-description 'line-thickness number? "the thickness[stafflinethickness] of the line.") (grob-property-description 'lookup symbol? "lookup method: 'value for plain text, 'name for character-name.") +(grob-property-description 'longa-cluster-size integer? "In mensural style notation, number of rest symbols that should be clustered within a zig-zag line of longa rests.") (grob-property-description 'magnify number? "the magnification factor. FIXME: doesn't work for feta fonts.") (grob-property-description 'maximum-duration-for-spacing moment? "space as if a duration of this type is available in this measure.") (grob-property-description 'maximum-length number? "don't make Grob longer than this")