From 5615f63e05982524c3d3ac39c47f10813626681a Mon Sep 17 00:00:00 2001 From: fred Date: Sun, 24 Mar 2002 19:48:48 +0000 Subject: [PATCH] lilypond-0.0.76 --- COPYING | 5 +- init/engraver.ini | 110 ++++--- lily/include/lyric-engraver.hh | 5 +- lily/lyric-grav.cc | 60 ++++ lily/score-grav.cc | 42 +-- lily/spring-spacer.cc | 581 +++++++++++++++++++++++++++++++++ 6 files changed, 734 insertions(+), 69 deletions(-) create mode 100644 lily/lyric-grav.cc create mode 100644 lily/spring-spacer.cc diff --git a/COPYING b/COPYING index 54b93e6973..da81602209 100644 --- a/COPYING +++ b/COPYING @@ -1,10 +1,7 @@ This license applies to all files except: - the included input files which explicitly state a different -copyright - - - the MetaFont sources, found in the subdirectory mf/. See -mf/README. +copyright policy GNU GENERAL PUBLIC LICENSE diff --git a/init/engraver.ini b/init/engraver.ini index fac0e15c06..4d73ecf419 100644 --- a/init/engraver.ini +++ b/init/engraver.ini @@ -2,7 +2,68 @@ % setup for Request->Element conversion. Guru-only % -\requesttranslator { +staff_engraver = \requesttranslator { + Engraver "Engraver_group_engraver" + \alias "Staff"; + \consists "Line_group_engraver"; + \consists "Bar_engraver"; + \consists "Clef_engraver"; + \consists "Key_engraver"; + \consists "Meter_engraver"; + \consists "Local_key_engraver"; + \consists "Staff_sym_engraver"; + \consists "Collision_engraver"; + \consists "Rest_collision_engraver"; + \contains \requesttranslator { + Engraver "Voice_group_engravers" + \alias "Voice_group"; + \consists "Dynamic_engraver"; + \consists "Stem_beam_engraver"; + \consists "Script_engraver"; + \consists "Note_column_engraver"; + \consists "Slur_engraver"; + \contains \requesttranslator { + Engraver "Engraver_group_engraver" + \alias "Voice"; + \consists "Note_head_engraver" ; + \consists "Tie_engraver"; + } + } + } + +piano_staff_engraver = \requesttranslator { + Engraver "Engraver_group_engraver" + \alias "Piano"; + \alias "Hoenoemjedat"; + \consists "Span_bar_engraver"; + \consists "Vertical_align_engraver"; + \consists "Line_group_engraver"; + \consists "Piano_bar_engraver"; + \contains\requesttranslator { \staff_engraver } +} + +staff_group_engraver = \requesttranslator { + Engraver "Engraver_group_engraver" + \alias "Staff_group"; + \consists "Span_bar_engraver"; + \consists "Vertical_align_engraver"; + \consists "Line_group_engraver"; + \contains\requesttranslator { \staff_engraver } +} +lyric_engraver = \requesttranslator { + Engraver "Engraver_group_engraver" + \alias "Lyric"; + + \contains\requesttranslator{ + Engraver "Engraver_group_engraver" + \consists "Lyric_engraver"; + \consists "Line_group_engraver"; + \consists "Swallow_engraver"; + } + \consists "Vertical_align_engraver"; +} + +orchestral_score_translator = \requesttranslator { Engraver Score_engraver \alias "Score"; @@ -11,51 +72,14 @@ \consists "Key_align_engraver"; \consists "Meter_align_engraver"; \consists "Score_horizontal_align_engraver"; - \consists "Vertical_align_engraver"; - \consists "Span_bar_engraver"; + \consists "Span_score_bar_engraver"; - \contains\requesttranslator { - Engraver "Staff_engravers" - \alias "Staff"; - \consists "Bar_engraver"; - \consists "Clef_engraver"; - \consists "Key_engraver"; - \consists "Meter_engraver"; - \consists "Local_key_engraver"; - \consists "Staff_sym_engraver"; - \consists "Collision_engraver"; - \consists "Rest_collision_engraver"; - \contains \requesttranslator { - Engraver "Voice_group_engravers" - \alias "Voice_group"; - \consists "Dynamic_engraver"; - \consists "Stem_beam_engraver"; - \consists "Script_engraver"; - \consists "Note_column_engraver"; - \consists "Slur_engraver"; - \contains \requesttranslator { - Engraver "Engraver_group_engraver" - \alias "Voice"; - \consists "Note_head_engraver" ; - \consists "Tie_engraver"; - } - } - } + \contains \requesttranslator { \staff_group_engraver } + \contains \requesttranslator { \lyric_engraver } + \contains \requesttranslator { \piano_staff_engraver } - \contains\requesttranslator { - Engraver "Staff_engravers" - \alias "Lyric"; - \contains\requesttranslator{ - Engraver "Voice_group_engravers" - \alias "Voice_group"; - \contains\requesttranslator{ - Engraver "Engraver_group_engraver" - \consists "Lyric_engraver"; - \consists "Swallow_engraver"; - } - } - } + } diff --git a/lily/include/lyric-engraver.hh b/lily/include/lyric-engraver.hh index 9c50d5c068..9c1aefadac 100644 --- a/lily/include/lyric-engraver.hh +++ b/lily/include/lyric-engraver.hh @@ -15,7 +15,10 @@ #include "lily-proto.hh" class Lyric_engraver : public Engraver { - Array lreq_arr_; + Lyric_req* lreq_l_; + Text_item *lyric_item_p_; +protected: + virtual void do_pre_move_processing(); virtual bool do_try_request(Request*); virtual void do_process_requests(); virtual void do_post_move_processing(); diff --git a/lily/lyric-grav.cc b/lily/lyric-grav.cc new file mode 100644 index 0000000000..08c81723a0 --- /dev/null +++ b/lily/lyric-grav.cc @@ -0,0 +1,60 @@ +/* + lyric-engraver.cc -- implement Lyric_engraver + + source file of the GNU LilyPond music typesetter + + (c) 1997 Han-Wen Nienhuys +*/ + +#include "lyric-engraver.hh" +#include "musical-request.hh" +#include "text-item.hh" +#include "paper-def.hh" +#include "lookup.hh" + +Lyric_engraver::Lyric_engraver() +{ + lreq_l_ =0; + lyric_item_p_ =0; +} + +bool +Lyric_engraver::do_try_request(Request*r) +{ + Musical_req * m =r->musical(); + if (!m || ! m->lreq_l()) + return false; + lreq_l_ = m->lreq_l(); + + return true; +} + +void +Lyric_engraver::do_process_requests() +{ + if ( lreq_l_ ) { + lyric_item_p_ = new Text_item(lreq_l_->tdef_p_ ); + lyric_item_p_->dir_i_ = -1; + lyric_item_p_->fat_b_ = true; + announce_element( Score_elem_info( lyric_item_p_, lreq_l_)); + } +} + +void +Lyric_engraver::do_post_move_processing() +{ + lreq_l_ =0; +} + +void +Lyric_engraver::do_pre_move_processing() +{ + if ( lyric_item_p_ ){ + typeset_element( lyric_item_p_); + lyric_item_p_ =0; + } +} + +IMPLEMENT_STATIC_NAME(Lyric_engraver); +IMPLEMENT_IS_TYPE_B1(Lyric_engraver,Engraver); +ADD_THIS_ENGRAVER(Lyric_engraver); diff --git a/lily/score-grav.cc b/lily/score-grav.cc index f837dba557..bcec7e71fb 100644 --- a/lily/score-grav.cc +++ b/lily/score-grav.cc @@ -17,6 +17,7 @@ #include "score.hh" #include "musical-request.hh" #include "score-column.hh" +#include "command-request.hh" void @@ -28,28 +29,29 @@ Score_engraver::set_score(Score *s) Score_engraver::Score_engraver() { + disallow_break_b_ = false; scoreline_l_ =0; command_column_l_ =0; musical_column_l_ =0; + breaks_i_ =0; } void Score_engraver::prepare(Moment w) { - Score_column* c1 = new Score_column(w); - Score_column* c2 = new Score_column(w); + command_column_l_ = new Score_column(w); + musical_column_l_ = new Score_column(w); - c1->musical_b_ = false; - c2->musical_b_ = true; + command_column_l_->musical_b_ = false; + musical_column_l_->musical_b_ = true; - score_l_->cols_.bottom().add(c1); - score_l_->cols_.bottom().add(c2); - set_cols(c1,c2); - - + score_l_->pscore_p_->add(command_column_l_); + score_l_->pscore_p_->add(musical_column_l_); + disallow_break_b_ = false; post_move_processing(); } + void Score_engraver::finish() { @@ -65,13 +67,6 @@ Score_engraver::do_creation_processing() Engraver_group_engraver::do_creation_processing(); } -void -Score_engraver::set_cols(Score_column*c1,Score_column*c2) -{ - command_column_l_ = c1; - musical_column_l_ = c2; -} - void Score_engraver::do_removal_processing() { @@ -93,12 +88,9 @@ Score_engraver::process() void Score_engraver::announce_element(Score_elem_info info) { + announce_info_arr_.push(info); info.origin_grav_l_arr_.push(this); - if (info.elem_l_->name() == Bar::static_name()) { - get_staff_info().command_pcol_l()->set_breakable(); - } - announce_info_arr_.push(info); } void Score_engraver::do_announces() @@ -118,7 +110,7 @@ Score_engraver::do_announces() */ if (announce_info_arr_[i].req_l_) { Musical_req *m = announce_info_arr_[i].req_l_->musical(); - if (m&&m->rhythmic()) { + if (m && m->rhythmic()) { musical_column_l_->add_duration( m->duration()); } } @@ -172,6 +164,12 @@ Score_engraver::typeset_all() void Score_engraver::do_pre_move_processing() { + if ( !disallow_break_b_ ){ + get_staff_info().command_pcol_l()->set_breakable(); + breaks_i_ ++; + if ( ! (breaks_i_%8)) + *mlog << "[" << breaks_i_ << "]" << flush; + } // this generates all items. Engraver_group_engraver::do_pre_move_processing(); @@ -204,6 +202,8 @@ Score_engraver::do_try_request(Request*r) for ( int i =0; !gotcha && i < nongroup_l_arr_.size() ; i++) gotcha = nongroup_l_arr_[i]->try_request(r); + if ( r->command() && r->command()->disallowbreak()) + disallow_break_b_ = true; return gotcha; } diff --git a/lily/spring-spacer.cc b/lily/spring-spacer.cc new file mode 100644 index 0000000000..0804161edd --- /dev/null +++ b/lily/spring-spacer.cc @@ -0,0 +1,581 @@ +/* + spring-spacer.cc -- implement Spring_spacer + + source file of the GNU LilyPond music typesetter + + (c) 1996,1997 Han-Wen Nienhuys +*/ + + +#include +#include "spring-spacer.hh" +#include "p-col.hh" +#include "debug.hh" +#include "qlp.hh" +#include "unionfind.hh" +#include "idealspacing.hh" +#include "pointer.tcc" +#include "score-column.hh" +#include "paper-def.hh" +#include "dimen.hh" +#include "minterval.hh" + +Vector +Spring_spacer::default_solution()const +{ + return try_initial_solution() ; +} + +Score_column* +Spring_spacer::scol_l(int i) +{ + return (Score_column*)cols[i].pcol_l_; +} + +const Real COLFUDGE=1e-3; +template class P; // ugh. + +bool +Spring_spacer::contains(PCol const *w) +{ + for (int i=0; i< cols.size(); i++) + if (cols[i].pcol_l_ == w) + return true; + return false; +} + + +void +Spring_spacer::OK() const +{ +#ifndef NDEBUG + for (int i = 1; i < cols.size(); i++) + assert(cols[i].rank_i_ > cols[i-1].rank_i_); + for (int i = 1; i < loose_col_arr_.size(); i++) + assert(loose_col_arr_[i].rank_i_ > loose_col_arr_[i-1].rank_i_); +#endif +} + +/** + Make sure no unconnected columns happen. + */ +void +Spring_spacer::handle_loose_cols() +{ + Union_find connected(cols.size()); + Array fixed; + for (PCursor i(ideal_p_list_.top()); i.ok(); i++){ + connected.connect(i->left_i_,i->right_i_); + } + for (int i = 0; i < cols.size(); i++) + if (cols[i].fixed()) + fixed.push(i); + for (int i=1; i < fixed.size(); i++) + connected.connect(fixed[i-1], fixed[i]); + + for (int i = cols.size(); i--; ) { + if (! connected.equiv(fixed[0], i)) { + warning("unconnected column: " + String(i)); + loosen_column(i); + } + } + OK(); +} + + +/** + Guess a stupid position for loose columns. Put loose columns at + regular distances from enclosing calced columns + */ +void +Spring_spacer::position_loose_cols(Vector &sol_vec)const +{ + if (!loose_col_arr_.size()) + return ; + assert(sol_vec.dim()); + Array fix_b_arr; + fix_b_arr.set_size(cols.size() + loose_col_arr_.size()); + Real utter_right_f=-INFTY; + Real utter_left_f =INFTY; + for (int i=0; i < loose_col_arr_.size(); i++) { + fix_b_arr[loose_col_arr_[i].rank_i_] = false; + } + for (int i=0; i < cols.size(); i++) { + int r= cols[i].rank_i_; + fix_b_arr[r] = true; + utter_right_f = utter_right_f >? sol_vec(i); + utter_left_f = utter_left_f 0) ?sol_vec(j-1) : utter_left_f; + Real right_pos_f = + (j < sol_vec.dim()) ? sol_vec(j) : utter_right_f; + int left_rank = (j>0) ? cols[j-1].rank_i_ : 0; + int right_rank = (j left_rank && r < right_rank); + + v(i) = (r - left_rank)*left_pos_f/ d_r + + (right_rank - r) *right_pos_f /d_r; + } + } + sol_vec = v; +} + +bool +Spring_spacer::check_constraints(Vector v) const +{ + int dim=v.dim(); + assert(dim == cols.size()); + + for (int i=0; i < dim; i++) { + + if (cols[i].fixed()&& + abs(cols[i].fixed_position() - v(i)) > COLFUDGE) + return false; + + if (!i) + continue; + + Real mindist=cols[i-1].minright() + +cols[i].minleft(); + + // ugh... compares + Real dif =v(i) - v(i-1)- mindist; + bool b = (dif > - COLFUDGE); + + + if (!b) + return false; + + } + return true; +} + +bool +Spring_spacer::check_feasible() const +{ + Vector sol(try_initial_solution()); + return check_constraints(sol); +} + +/// generate a solution which obeys the min distances and fixed positions +Vector +Spring_spacer::try_initial_solution() const +{ + int dim=cols.size(); + Vector initsol(dim); + for (int i=0; i < dim; i++) { + if (cols[i].fixed()) { + initsol(i)=cols[i].fixed_position(); + + if (i > 0) { + Real r =initsol(i-1) + cols[i-1].minright(); + if (initsol(i) < r ) { + warning("overriding fixed position"); + initsol(i) =r; + } + } + + } else { + Real mindist=cols[i-1].minright() + +cols[i].minleft(); + if (mindist < 0.0) + warning("Excentric column"); + initsol(i)=initsol(i-1)+mindist; + } + } + + return initsol; +} + + + +Vector +Spring_spacer::find_initial_solution() const +{ + Vector v(try_initial_solution()); + assert(check_constraints(v)); + return v; +} + +// generate the matrices +void +Spring_spacer::make_matrices(Matrix &quad, Vector &lin, Real &c) const +{ + quad.fill(0); + lin.fill(0); + c = 0; + + for (PCursor i(ideal_p_list_.top()); i.ok(); i++) { + int l = i->left_i_; + int r = i->right_i_; + + quad(r,r) += i->hooke_f_; + quad(r,l) -= i->hooke_f_; + quad(l,r) -= i->hooke_f_; + quad(l,l) += i->hooke_f_; + + lin(r) -= i->space_f_*i->hooke_f_; + lin(l) += i->space_f_*i->hooke_f_; + + c += sqr(i->space_f_); + } +} + +// put the constraints into the LP problem +void +Spring_spacer::make_constraints(Mixed_qp& lp) const +{ + int dim=cols.size(); + for (int j=0; j < dim; j++) { + Colinfo c=cols[j]; + if (c.fixed()) { + lp.add_fixed_var(j,c.fixed_position()); + } + if (j > 0){ + Vector c1(dim); + + c1(j)=1.0 ; + c1(j-1)=-1.0 ; + lp.add_inequality_cons(c1, cols[j-1].minright() + + cols[j].minleft()); + } + } +} + +Array +Spring_spacer::solve() const +{ + assert(check_feasible()); + + Mixed_qp lp(cols.size()); + make_matrices(lp.quad,lp.lin, lp.const_term); + make_constraints(lp); + Vector start=find_initial_solution(); + Vector sol(lp.solve(start)); + if (!check_constraints(sol)) { + WARN << "solution doesn't satisfy constraints.\n" ; + } + Real energy_f =lp.eval(sol); + position_loose_cols(sol); + + Array posns(sol); + + posns.push(energy_f); + return posns; +} + +/** + add one column to the problem. +*/ +void +Spring_spacer::add_column(PCol *col, bool fixed, Real fixpos) +{ + Colinfo c(col,(fixed)? &fixpos : 0); + if (cols.size()) + c.rank_i_ = cols.top().rank_i_+1; + else + c.rank_i_ = 0; + cols.push(c); +} + +Array +Spring_spacer::error_pcol_l_arr()const +{ + Array retval; + for (int i=0; i< cols.size(); i++) + if (cols[i].ugh_b_) + retval.push(cols[i].pcol_l_); + for (int i=0; i < loose_col_arr_.size(); i++) { + retval.push(loose_col_arr_[i].pcol_l_); + } + return retval; +} + +void +Spring_spacer::loosen_column(int i) +{ + Colinfo c=cols.get(i); + for (PCursor j(ideal_p_list_.top()); j.ok(); j++){ + if (j->left_i_ == i|| j->right_i_ == i) + j.del(); + else + j++; + } + c.ugh_b_ = true; + + int j=0; + for (; j < loose_col_arr_.size(); j++) { + if (loose_col_arr_[j].rank_i_ > c.rank_i_) + break; + } + loose_col_arr_.insert(c,j); +} + + +void +Spring_spacer::print() const +{ +#ifndef NPRINT + for (int i=0; i < cols.size(); i++) { + mtor << "col " << i<<' '; + cols[i].print(); + } + for (PCursor i(ideal_p_list_.top()); i.ok(); i++){ + i->print(); + } +#endif + +} + + +void +Spring_spacer::connect(int i, int j, Real d, Real h) +{ + Idealspacing * s = new Idealspacing; + s->left_i_ = i; + s->right_i_ = j; + s->space_f_ = d; + s->hooke_f_ = h; + + ideal_p_list_.bottom().add(s); +} + +/** + walk through all durations in all Score_columns + */ +struct Durations_iter +{ + Spring_spacer * sp_l_; + int col_i_; + int d_i_; + + Durations_iter(Spring_spacer*); + + Moment duration()const; + Moment when()const; + + bool ok()const; + void next(); +}; + +Durations_iter::Durations_iter(Spring_spacer * s) +{ + col_i_ =0; + d_i_ =0; // ugh + + sp_l_ = s; + if (! sp_l_->scol_l(col_i_)->durations.size() ) + next(); +} + +Moment +Durations_iter::duration() const +{ + return sp_l_->scol_l(col_i_)->durations[d_i_]; +} + +bool +Durations_iter::ok()const{ + return col_i_ < sp_l_->cols.size(); +} + +Moment +Durations_iter::when()const{ + return sp_l_->scol_l(col_i_)->when(); +} + +void +Durations_iter::next() +{ + d_i_ ++; + while ( col_i_ < sp_l_->cols.size() + && d_i_ >= sp_l_->scol_l(col_i_)->durations.size()){ + col_i_ ++; + d_i_ =0; + } +} + + +/** + generate springs between columns. + + UNDER DESTRUCTION + + TODO: This needs rethinking. Spacing should take optical + effects into account, and should be local (measure wide) + + The algorithm is taken from : + + John S. Gourlay. ``Spacing a Line of Music,'' Technical Report + OSU-CISRC-10/87-TR35, Department of Computer and Information + Science, The Ohio State University, 1987. + + */ +void +Spring_spacer::calc_idealspacing() +{ + + for (int i=0; i < cols.size(); i++) + scol_l(i)->preprocess(); + + /* get the shortest running note at a time. */ + Array shortest_arr_; + { + Durations_iter d_iter(this); + for (int i=0; i < cols.size(); i++) { + Moment now = scol_l(i)->when(); + while ( d_iter.ok() && now >= d_iter.when() ) { + if ( now < d_iter.when() + d_iter.duration()) + break; + d_iter.next(); + } + if ( d_iter.ok() && now >= d_iter.when()) { + Durations_iter d2 = d_iter; + Moment shortest = INFTY; + while (d2.ok() && d2.when() <= now) { + shortest = shortest ideal_arr_; + Array hooke_arr_; + for (int i=0; i < cols.size(); i++){ + ideal_arr_.push( -1.0); + hooke_arr_.push(1.0); + } + + for (int i=0; i < cols.size(); i++) { + if ( !scol_l(i)->musical_b()) { + ideal_arr_[i] = cols[i].minright() + 2 PT; + hooke_arr_[i] = 2.0; + if (i+1 < cols.size()) { + Moment delta_t = scol_l(i+1)->when() - scol_l(i)->when() ; + Real dist = delta_t ? paper_l()->duration_to_dist(delta_t) : 0; + if (delta_t && dist > ideal_arr_[i]) + ideal_arr_[i] = dist; + } + } + } + for (int i=0; i < cols.size(); i++) { + if (scol_l(i)->musical_b()) { + Moment shortest_len = shortest_arr_[i]; + if ( ! shortest_len ) { + warning( "Can't find a ruling note at " + +String( scol_l(i)->when())); + shortest_len = 1; + } + Moment delta_t = scol_l(i+1)->when() - scol_l(i)->when(); + Real dist = paper_l()->duration_to_dist(shortest_len); + dist *= delta_t / shortest_len; + if (!scol_l(i+1)->musical_b() ) { + + if (ideal_arr_[i+1] + cols[i+1].minleft() < dist) { + ideal_arr_[i+1] = dist/2 + cols[i+1].minleft(); + hooke_arr_[i+1] =1.0; + } + ideal_arr_[i] = dist/2; + } else + ideal_arr_[i] = dist; + } + } + + for (int i=0; i < ideal_arr_.size()-1; i++) { + assert (ideal_arr_[i] >=0 && hooke_arr_[i] >=0); + connect(i, i+1, ideal_arr_[i], hooke_arr_[i]); + } + +} + + + +void +Spring_spacer::prepare() +{ + calc_idealspacing(); + handle_loose_cols(); + print(); +} + +Line_spacer* +Spring_spacer::constructor() +{ + return new Spring_spacer; +} + +#if 0 +void obsolete() +{ + for (int i=0; i < cols.size(); i++) { + if (!scol_l(i)->used_b()) + continue; + + + int j = i+1; + + if (scol_l(i)->musical_b()) { + assert ( j < cols.size()); + + for (int n=0; n < scol_l(i)->durations.size(); n++) { + Moment d = scol_l(i)->durations[n]; + Real dist = paper_l()->duration_to_dist(d); + Real strength = scol_l(i)->durations[0]/scol_l(i)->durations[n]; + assert(strength <= 1.0); + + while (j < cols.size()) { + if (scol_l(j)->used_b() + && scol_l(j)->when() >= d + scol_l(i)->when() ) + break; + j++; + } + if ( j < cols.size() ){ + Moment delta_desired = scol_l(j)->when() - (d+scol_l(i)->when()); + dist += paper_l()->duration_to_dist(delta_desired); + if (scol_l(j)->musical_b()) { + dist += cols[j].minleft() + 2 PT; + } + connect(i, j, dist, strength); + } + } + } else if (j < cols.size()) { + while (!scol_l(j)->used_b()) + j++; + + /* attach i to the next column in use. This exists, since + the last col is breakable, and therefore in use + */ + + Moment d = scol_l(j)->when() - scol_l(i)->when(); + Real minimal_f = cols[i].minright() +cols[j].minleft() + 2 PT; + Real durdist_f = (d) ? paper_l()->duration_to_dist(d) : 0; // todo + + connect(i, j, minimal_f