diff options
Diffstat (limited to 'src')
62 files changed, 5315 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000000..65374e807e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,8 @@ +default: + $(MAKE) -C .. + +dist: + ln Makefile $(mycc) $(othersrc) $(DDIR)/src + +TAGS: $(mycc) + etags -CT $(mycc) diff --git a/src/bar.cc b/src/bar.cc new file mode 100644 index 0000000000..bb66f2b777 --- /dev/null +++ b/src/bar.cc @@ -0,0 +1,18 @@ +#include "bar.hh" +#include "string.hh" +#include "molecule.hh" +#include "paper.hh" +#include "lookup.hh" + + +Bar::Bar( String t) +{ + type = t; +} +void +Bar::preprocess() +{ + Symbol s = paper()->lookup_->bar(type); + output = new Molecule(Atom(s)); +} + diff --git a/src/beam.cc b/src/beam.cc new file mode 100644 index 0000000000..49214c91f2 --- /dev/null +++ b/src/beam.cc @@ -0,0 +1,179 @@ +#include "beam.hh" +#include "debug.hh" +#include "symbol.hh" +#include "molecule.hh" +#include "leastsquares.hh" +#include "pcol.hh" +#include "stem.hh" +#include "paper.hh" +#include "lookup.hh" + + +struct Stem_info { + Real x; + Real idealy; + Real miny; + int no_beams; + + Stem_info(){} + Stem_info(const Stem*); +}; +Stem_info::Stem_info(const Stem*s) +{ + x = s->hpos(); + int dir = s->dir; + idealy = MAX(dir*s->top, dir*s->bot); + miny = MAX(dir*s->minnote, dir*s-> maxnote); + assert(miny <= idealy); + no_beams = s->flag; +} + +/****************/ + +Beam::Beam() +{ + slope = 0; + left_pos = 0.0; + dir =0; +} + +void +Beam::add(Stem*s) +{ + stems.bottom().add(s); + s->print_flag = false; +} + +void +Beam::set_default_dir() +{ + int dirs[2]; + dirs[0]=0; dirs[1] =0; + for (PCursor<Stem*> sc(stems); sc.ok(); sc++) { + sc->set_default_dir(); + dirs[(sc->dir+1)/2] ++; + } + dir = (dirs[0] > dirs[1]) ? -1 : 1; + for (PCursor<Stem*> sc(stems); sc.ok(); sc++) { + sc->dir = dir; + } +} + +void +Beam::solve_slope() +{ + svec<Stem_info> sinfo; + for (PCursor<Stem* >sc(stems); sc.ok(); sc++) { + sc->set_default_extents(); + Stem_info i(sc); + sinfo.add(i); + } + Real leftx = sinfo[0].x; + Least_squares l; + for (int i=0; i < sinfo.sz(); i++) { + sinfo[i].x -= leftx; + l.input.add(Offset(sinfo[i].x, sinfo[i].idealy)); + } + + l.minimise(slope, left_pos); + Real dy = 0.0; + for (int i=0; i < sinfo.sz(); i++) { + Real y = sinfo[i].x * slope + left_pos; + Real my = sinfo[i].miny; + + if (my - y > dy) + dy = my -y; + } + left_pos += dy; + left_pos *= dir; + slope *= dir; + + {Real inter =paper()->interline()/2; + Real unitslope = slope*inter; + + // set beamslope, for setting stems correctly + // ignoring return. + Symbol sy = paper()->lookup_->beam(unitslope, width().length()); + slope =unitslope / inter; + } +} + +void +Beam::set_stemlens() +{ + PCursor<Stem*> s(stems); + Real x0 = s->hpos(); + for (; s.ok() ; s++) { + Real x = s->hpos()-x0; + s->set_stemend(left_pos + slope * x); + } +} + +void +Beam::calculate() +{ + assert(stems.size()>1); + if (!dir) + set_default_dir(); + + solve_slope(); + set_stemlens(); +} + +void +Beam::process() +{ + calculate(); + brew_molecule(); +} + + +// todo. +Spanner * +Beam::broken_at(const PCol *, const PCol *) const +{ + return new Beam(*this); +} + +void +Beam::preprocess() +{ + left = (*stems.top()) ->pcol_; + right = (*stems.bottom())->pcol_; +} + +Interval +Beam::height() const +{ + return output->extent().y; +} + +Interval +Beam::width() const +{ + Beam * me = (Beam*) this; // ugh + return Interval( (*me->stems.top()) ->hpos(), + (*me->stems.bottom()) ->hpos() ); +} + +void +Beam::brew_molecule() +{ + Real inter=paper()->interline()/2; + Real sl = slope*inter; + Real w = width().length() + paper()->rule_thickness(); + Symbol s = paper()->lookup_->beam(sl,w); + + Atom a(s); + + Real dx = width().min -left->hpos; + a.translate(Offset(dx,left_pos*inter)); + output = new Molecule(a); +} + +void +Beam::print()const +{ + mtor << "Beam, slope " <<slope << "left ypos " << left_pos<<'\n'; +} + diff --git a/src/boxes.cc b/src/boxes.cc new file mode 100644 index 0000000000..ebfc08abfe --- /dev/null +++ b/src/boxes.cc @@ -0,0 +1,31 @@ +#include "boxes.hh" +#include "const.hh" + +void +Interval::set_empty() { + min = INFTY; + max = -INFTY; +} +Real +Interval::length() const { + return max-min; +} +Box::Box(svec<Real> s) +{ + assert(s.sz() == 4); + x.min = s[0]; + x.max = s[1]; + y.min = s[2]; + y.max = s[3]; +} + +Box::Box() +{ +} + +Box::Box(Interval ix, Interval iy) +{ + x = ix; + y = iy; +} + diff --git a/src/break.cc b/src/break.cc new file mode 100644 index 0000000000..de9a704984 --- /dev/null +++ b/src/break.cc @@ -0,0 +1,135 @@ +/* + do calculations for breaking problem + + */ +#include "paper.hh" +#include "linespace.hh" +#include "debug.hh" +#include "scoreline.hh" +#include "pscore.hh" + +// construct an appropriate Spacing_problem and solve it. +svec<Real> +PScore::solve_line(svec<const PCol *> curline) const +{ + Spacing_problem sp; + + sp.add_column(curline[0], true, 0.0); + for (int i=1; i< curline.sz()-1; i++) + sp.add_column(curline[i]); + sp.add_column(curline.last(), true, paper_->linewidth); + + // misschien moeven uit Spacing_problem? + for (PCursor<Idealspacing *> i(suz); i.ok(); i++) { + sp.add_ideal(i); + } + svec<Real> the_sol=sp.solve(); + return the_sol; +} + +bool +PScore::feasible(svec<const PCol *> curline) const +{ + Real l =0; + for (int i=0; i < curline.sz(); i++) + l +=curline[i]->width().length(); + return l < paper_->linewidth; +} + +void +PScore::problem_OK() const +{ + if (!cols.size()) + error("PScore::problem_OK(): Score does not have any columns"); + PCursor<PCol *> start(cols); + PCursor<PCol *> end (((PScore*)this)->cols.bottom()); + + assert(start->breakable()); + assert(end->breakable()); +} + +struct Col_configuration { + svec<const PCol*> line; + svec<Real> config; + Real energy; + + Col_configuration() { + energy = INFTY; + } + void add(const PCol*c) { line.add(c);} + void setsol(svec<Real> sol) { + config = sol; + energy = config.last(); + config.pop(); + } + void print() const { +#ifndef NPRINT + mtor << "energy : " << energy << '\n'; + mtor << "line of " << config.sz() << " cols\n"; +#endif + } +}; + +/// wordwrap type algorithm +/* el stupido. This should be done more accurately: + + It would be nice to have a Dynamic Programming type of algorithm + similar to TeX's + + */ + +void +PScore::calc_breaking() +{ + OK(); + problem_OK(); + PCursor<PCol *> curcol(cols); + + svec<const PCol *> breakpoints(find_breaks()); + assert(breakpoints.sz()>=2); + for (int i=0 ; i < breakpoints.sz() -1; ) { + Col_configuration minimum; + Col_configuration current; + + // do another line + PCol *post = breakpoints[i]->postbreak; + current.add( post); + curcol++; // skip the breakable. + i++; + + while (i < breakpoints.sz()) { + + // add another measure. + while(breakpoints[i] !=curcol){ + + current.add(curcol); + curcol++; + } + current.add(breakpoints[i]->prebreak ); + if (!feasible(current.line)) { + if (!minimum.line.sz()) + error("sorry, this measure is too long"); + break; + } + current.setsol(solve_line(current.line)); + current.print(); + + if (current.energy < minimum.energy) { + minimum = current; + } else { // we're one col too far. + i--; + while (curcol != breakpoints[i]) + curcol --; + + break; + } + + current.line.last()=breakpoints[i]; + curcol ++; + i++; + } + mtor << "Adding line, next breakpoint " << i << '\n'; + add_line(minimum.line, minimum.config); + } +} + diff --git a/src/calcideal.cc b/src/calcideal.cc new file mode 100644 index 0000000000..8099e0e4c7 --- /dev/null +++ b/src/calcideal.cc @@ -0,0 +1,65 @@ +#include "tstream.hh" +#include "score.hh" +#include "pscore.hh" +#include "staff.hh" +#include "paper.hh" +#include "sccol.hh" +#include "debug.hh" + + +void +Score::do_connect(PCol *c1, PCol *c2, Real d) +{ + Idealspacing*sp=pscore_->get_spacing(c1,c2); + + if (!sp->hooke){ + sp->hooke = 1.0; + sp->space =d; + } +} + +void +Score::connect_nonmus(PCol* c1, PCol *c2, Real d) +{ + if (c2->used && c1->used) { + do_connect(c1,c2,d); + + // alert! this is broken! + if (c1->breakable()) { + do_connect(c1->postbreak, c2,d); + } + if (c2->breakable()) { + do_connect(c1, c2->prebreak,d); + } + if (c1->breakable() &&c2->breakable()) { + do_connect(c1->postbreak, c2->prebreak,d); + } + } +} +/* this needs A LOT of rethinking. + + generate springs between columns. + */ +void +Score::calc_idealspacing() +{ + PCursor<Score_column*> sc(cols_); + + for (; sc.ok(); sc++) { + if (sc->musical) + for (int i=0; i < sc->durations.sz(); i++) { + Real d = sc->durations[i]; + Real dist = paper_->duration_to_dist(d); + PCol * c2 = find_col(sc->when + d,true)->pcol; + connect_nonmus(sc->pcol, c2, dist); + c2 = find_col(sc->when + d,false)->pcol; + connect_nonmus(sc->pcol, c2, dist); + } + else if (sc->used()) { // ignore empty columns + PCol * c2 = find_col(sc->when,true)->pcol; + connect_nonmus(sc->pcol,c2,0.0); + } + } +} + + diff --git a/src/command.cc b/src/command.cc new file mode 100644 index 0000000000..23edf1ec36 --- /dev/null +++ b/src/command.cc @@ -0,0 +1,39 @@ +#include "string.hh" +#include "debug.hh" +#include "command.hh" + +bool +Command::isbreak()const +{ + return (code >= BREAK_PRE && code <= BREAK_END); +} + +Command::Command() +{ + code = NOP; + when = -1; + priority=0; +} + + + +Command::Command(Real w) +{ + code = NOP; + when = w; + priority=0; +} + +void +Command::print() const +{ +#ifndef NPRINT + mtor << "command at " << when << ", code " << code << " prio " << priority; + if (args.sz()) { + mtor<< " args: "; + for (int i = 0; i<args.sz(); i++) + mtor << "`"<<args[i] <<"',"; + } + mtor << "\n"; +#endif +} diff --git a/src/debug.cc b/src/debug.cc new file mode 100644 index 0000000000..d55c901945 --- /dev/null +++ b/src/debug.cc @@ -0,0 +1,27 @@ +#include <fstream.h> +#include <std/new.h> +#include "debug.hh" +#include "dstream.hh" +#include "vector.hh" + +Dstream monitor(&cout,".dstreamrc"); +ostream * nulldev = new ofstream("/dev/null"); + + +/* + want to do a stacktrace . + */ +void +mynewhandler() +{ + cerr << "Out of free store memory. Aborting.. "<< flush; + assert(false); +} + + +void +debug_init() +{ + set_new_handler(&mynewhandler); + set_matrix_debug(monitor); +} diff --git a/src/dimen.cc b/src/dimen.cc new file mode 100644 index 0000000000..eb53756b45 --- /dev/null +++ b/src/dimen.cc @@ -0,0 +1,40 @@ +#include <ctype.h> +#include "dimen.hh" +#include "debug.hh" +#include "string.hh" + +Real +parse_dimen(String dim) +{ + int i=dim.len()-1; + const char *s = dim; + while (i > 0 && (isspace(s[i]) || isalpha(s[i])) ){ + i--; + } + String unit(s + i+1); + return convert_dimen(dim.fvalue(), unit); +} + +const Real CM_TO_PT=72/2.54; + +Real +convert_dimen(Real quant, String unit) +{ + if (unit == "cm") + return quant * CM_TO_PT; + if (unit == "pt") + return quant; + if (unit == "mm") + return quant*CM_TO_PT/10; + if (unit == "in") + return quant * 72; + error ("unknown length unit: `" + unit+"'"); +} + +String +print_dimen(Real r) +{ + String s(r); + s += "pt "; + return s; +} diff --git a/src/getcommands.cc b/src/getcommands.cc new file mode 100644 index 0000000000..1f9362c59c --- /dev/null +++ b/src/getcommands.cc @@ -0,0 +1,58 @@ +#include "string.hh" +#include "parseconstruct.hh" +#include "command.hh" + +Command* +get_bar_command(Real w) +{ + Command*c = new Command; + c->when = w; + c->code = TYPESET; + c->args.add( "BAR"); + c->args.add( "|"); + c->priority = 100; + return c; +} + +Command * +get_meter_command(Real w, int n, int m) +{ + Command*c = new Command; + + c->when = w; + c->code = TYPESET; + c->args.add( "METER"); + c->args.add( n ); + c->args.add( m ); + c->priority = 50; // less than bar + return c; +} + +Command * +get_meterchange_command(int n, int m) +{ + Command*c = new Command; + + c->code = INTERPRET; + c->args.add( "METER"); + c->args.add( n ); + c->args.add( m ); + c->priority = 0; // more than bar + return c; +} + + +Command * +get_skip_command(int n, Real m) +{ + Command*c = new Command; + + c->code = INTERPRET; + c->args.add( "SKIP"); + c->args.add( n ); + c->args.add( m ); + c->priority = 0; // more than bar + return c; +} + + diff --git a/src/identifier.cc b/src/identifier.cc new file mode 100644 index 0000000000..024fe06486 --- /dev/null +++ b/src/identifier.cc @@ -0,0 +1,26 @@ +#include <assert.h> +#include "identifier.hh" +#include "staff.hh" +#include "lexer.hh" + + +Identifier::Identifier(String n) + :name (n) +{ + data = 0; +} + + +Identifier::~Identifier() +{ +} + +Staff_id::~Staff_id() +{ + delete staff(); +} + +Voice_id::~Voice_id() +{ + delete voice(); +} diff --git a/src/item.cc b/src/item.cc new file mode 100644 index 0000000000..914c924a99 --- /dev/null +++ b/src/item.cc @@ -0,0 +1,73 @@ +#include "pstaff.hh" +#include "pscore.hh" +#include "symbol.hh" +#include "molecule.hh" +#include "pcol.hh" + +void +Item::translate(Offset O) +{ + offset_ += O; +} + +void +Item::postprocess() +{ + // default: do nothing +} + + +void +Item::preprocess() +{ + // default: do nothing +} + +String +Item::TeXstring() const +{ + Item * me = (Item*) this; + output->translate(offset_); // ugh? + me ->offset_ = Offset(0,0); // URRGGH! + return output->TeXstring(); +} + +Interval +Item::width() const +{ + Interval i =output->extent().x ; + return i+=offset_.x; +} + +Interval +Item::height() const +{ + Interval i =output->extent().y; + return i+=offset_.y; +} + +Item::~Item() +{ + delete output; +} + +Item::Item() +{ + pcol_ = 0; + output = 0; + pstaff_ = 0; +} +void +Item::print() const +{ + assert(output); + output->print(); +} + +Paperdef* +Item::paper() const +{ + assert(pstaff_); + return pstaff_->pscore_->paper_; +} + diff --git a/src/keyword.cc b/src/keyword.cc new file mode 100644 index 0000000000..14903cfb61 --- /dev/null +++ b/src/keyword.cc @@ -0,0 +1,79 @@ +/* + keyword.cc -- keywords and identifiers + */ + +#include <stdlib.h> + +#include "glob.hh" +#include "lexer.hh" +//#include "mudobs.hh" +//#include "gram.hh" + +/* for the keyword table */ +struct Keyword_ent +{ + const char *name; + int tokcode; +}; + +struct Keyword_table +{ + Keyword_ent *table; + int maxkey; + Keyword_table(Keyword_ent *); + int lookup(const char *s) const; +}; + + +/* for qsort */ +int + tabcmp(const void * p1, const void * p2) +{ + return strcmp(((const Keyword_ent *) p1)->name, + ((const Keyword_ent *) p2)->name); +} + +Keyword_table::Keyword_table(Keyword_ent *tab) +{ + table = tab; + + /* count keywords */ + for (maxkey = 0; table[maxkey].name; maxkey++); + + /* sort them */ + qsort(table, maxkey, sizeof(Keyword_ent), tabcmp); +} + +/* + lookup with binsearch, return tokencode. +*/ +int +Keyword_table::lookup(const char *s)const +{ + int lo, + hi, + cmp, + result; + lo = 0; + hi = maxkey; + + /* binary search */ + do + { + cmp = (lo + hi) / 2; + + result = strcmp(s, table[cmp].name); + + if (result < 0) + hi = cmp; + else + lo = cmp; + } + while (hi - lo > 1); + if (!strcmp(s, table[lo].name)) + { + return table[lo].tokcode; + } else + return -1; /* not found */ +} + diff --git a/src/leastsquares.cc b/src/leastsquares.cc new file mode 100644 index 0000000000..1e1c91cee3 --- /dev/null +++ b/src/leastsquares.cc @@ -0,0 +1,25 @@ +#include "leastsquares.hh" + +void +Least_squares::minimise(Real &coef, Real &offset) +{ + Real sx = 0.0; + Real sy = 0.0; + Real sqx =0.0; + Real sxy = 0.0; + + for (int i=0; i < input.sz();i++) { + Real x=input[i].x; + Real y = input[i].y; + sx += x; + sy += y; + sqx += sqr(x); + sxy += x*y; + } + int N = input.sz(); + + + coef = (N * sxy - sx*sy )/(N*sqx - sqr(sx)); + offset = (sy - coef * sx)/N; + +} diff --git a/src/lexer.l b/src/lexer.l new file mode 100644 index 0000000000..1e3443e56e --- /dev/null +++ b/src/lexer.l @@ -0,0 +1,229 @@ +%{ // -*-Fundamental-*- + +#include <fstream.h> +#include <stdio.h> +#include "glob.hh" +#include "string.hh" + +#include "lexer.hh" +#include "keyword.hh" +#include "vray.hh" +#include "parser.hh" +#include "debug.hh" + +sstack<istream *> include_stack; +static int last_print; +const int DOTPRINT=50; // every 50 lines dots +%} + +%option c++ +%option noyywrap +%option nodefault +%option yylineno +%option debug +%x notes +%x incl +%x quote + + +NOTECOMMAND \\{WORD} +OPTSIGN !? +NOTENAMEI A|B|C|D|E|F|G|As|Bes|Ces|Des|Es|Fes|Ges|Ais|Bis|Cis|Dis|Eis|Fis|Gis +NOTENAMEII a|b|c|d|e|f|g|as|bes|ces|des|es|fes|ges|ais|bis|cis|dis|eis|fis|gis +NOTENAMEIII Ases|Beses|Ceses|Deses|Eses|Feses|Geses|Aisis|Bisis|Cisis|Disis|Eisis|Fisis|Gisis +NOTENAMEIIII ases|beses|ceses|deses|eses|feses|geses|aisis|bisis|cisis|disis|eisis|fisis|gisis +RESTNAME r|s +NOTENAME {NOTENAMEI}|{NOTENAMEII}|{NOTENAMEIII}|{NOTENAMEIIII} +PITCH ['`]*{OPTSIGN}{NOTENAME} +DURNAME 1|2|4|8|16|32 +DURATION {DURNAME}\.* +FULLNOTE {PITCH}{DURATION}? +WORD [a-zA-Z][a-zA-Z0-9_]+ +REAL [0-9]+(\.[0-9]*)? + +%% + +\$ { + BEGIN(notes); return '$'; +} + +<notes>{NOTECOMMAND} { + String c = YYText() +1; + int l = lookup_keyword(c); + if (l == -1) { + String e("unknown NOTECOMMAND: \\"); + e += c; + yyerror(e); + } + return l; +} + +<notes>{RESTNAME} { + const char *s = YYText(); + yylval.string = new String (s); + mtor << "rest:"<< yylval.string; + return RESTNAME; +} +<notes>{PITCH} { + const char *s = YYText(); + yylval.string = new String (s); + mtor << "pitch:"<< *yylval.string; + return PITCH; +} +<notes>{DURATION} { + yylval.string = new String (YYText()); + return DURATION; +} +<notes>\| { +} +<notes>[:space:]+ { +} +<notes>[ \t\n]+ { +} +<notes>%.* { + +} +<notes>\$ { + BEGIN(INITIAL); return '$'; +} +<notes>[\[){] { /* parens () are NO mistake */ + yylval.c = YYText()[0]; + return OPEN_REQUEST_PARENS; +} +<notes>[\]()}] { /* parens () are NO mistake */ + yylval.c = YYText()[0]; + return CLOSE_REQUEST_PARENS; +} + +<notes>. { + String s("lexer error: illegal character found: " + String(YYText())); + yyerror(s); +} + +\" { + BEGIN(quote); +} +<quote>[^\"]* { + yylval.string = new String (YYText()); +} +<quote>\" { + BEGIN(INITIAL); + return STRING; +} + +<<EOF>> { + if(!close_input()) + yyterminate(); +} +{WORD} { + int l = lookup_keyword(YYText()); + if (l != -1) + return l; + Identifier * id = lookup_identifier(YYText()); + if (id) { + yylval.id = id; + return IDENTIFIER; + } + String *sp = new String( YYText()); + mtor << "new id: " << *sp; + yylval.string=sp; + return NEWIDENTIFIER; +} + +{REAL} { + Real r; + int cnv=sscanf (YYText(), "%lf", &r); + assert(cnv == 1); + mtor << "token (REAL)" << r; + yylval.real = r; + return REAL; +} + +[\{\}\[\]\(\)] { + + mtor << "parens\n"; + return YYText()[0]; +} +[:=] { + char c = YYText()[0]; + mtor << "misc char" <<c<<"\n"; + return c; +} +[ \t\n]+ { + +} + +%.* { + //ignore +} +. { + error("lexer error: illegal character '"+String(YYText()[0])+ + "' encountered"); + return YYText()[0]; +} + +%% + +yyFlexLexer *lexer=0; + +// set the new input to s, remember old file. +void +new_input(String s) +{ + istream *newin ; + + if (s=="") + newin = &cin; + else + newin = new ifstream( s ); // + + if ( ! *newin) + error("cant open " + s); + cout << "["<<s<<flush; + + include_stack.push(newin); + + if (!lexer) { + lexer = new yyFlexLexer; + lexer->set_debug( !monitor.silence("Lexer")); + } + + lexer->switch_streams(newin); +} + + +// pop the inputstack. +bool +close_input() +{ + + istream *closing= include_stack.pop(); + if (closing != &cin) + delete closing; + + cout << "]" << flush; + + if (include_stack.empty()) { + return false ; + } else + lexer->switch_streams(include_stack.top()); + return true; +} + +int +yylex() { + return lexer->yylex(); +} + +void +yyerror(const char *s) +{ + *mlog << "error in line " << lexer->lineno() << ": " << s << '\n'; + exit(1); +} + +void +kill_lexer() +{ + delete lexer; +} diff --git a/src/linespace.cc b/src/linespace.cc new file mode 100644 index 0000000000..6b16c9afab --- /dev/null +++ b/src/linespace.cc @@ -0,0 +1,264 @@ +#include <math.h> +#include "linespace.hh" +#include "debug.hh" +#include "qlp.hh" +#include "unionfind.hh" + +const Real COLFUDGE=1e-3; +//#define COLFUDGE 1e-3 +bool +Spacing_problem::contains(const PCol *w) +{ + for (int i=0; i< cols.sz(); i++) + if (cols[i].pcol_ == w) + return true; + return false; +} + +int +Spacing_problem::col_id(const PCol *w)const +{ + for (int i=0; i< cols.sz(); i++) + if (cols[i].pcol_ == w) + return i; + assert(false); +} + +void +Spacing_problem::OK() const +{ +#ifndef NDEBUG + Union_find connected(cols.sz()); + svec<int> fixed; + for (int i=0; i < ideals.sz(); i++) { + assert(ideals[i]->hooke > 0); + int l = col_id(ideals[i]->left); + int r = col_id(ideals[i]->right); + connected.connect(l,r); + } + for (int i = 0; i < cols.sz(); i++) + if (cols[i].fixed) + fixed.add(i); + for (int i = 0; i < cols.sz(); i++) { + bool c=false; + for (int j =0; j<fixed.sz(); j++) + c |= connected.equiv(j,i); + assert(c); + } +#endif +} + +bool +Spacing_problem::check_constraints(Vector v) const +{ + int dim=v.dim(); + // mtor << "checking solution " << v << '\n'; + for (int i=0; i < dim; i++) { + + if (cols[i].fixed&& ABS(cols[i].fixpos - 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 1 + if (!b) + return false; + +#else + mtor << "dif= "<<dif<<" fudge= " << COLFUDGE<< " dif >fudge= "<< + b << "\n"; + + /* fucks up for unknown reasons */ + if (dif < -COLFUDGE) + return false; +#endif + + } + return true; +} + +bool +Spacing_problem::check_feasible() const +{ + Vector sol(try_initial_solution()); + return check_constraints(sol); +} + +// generate a solution which obeys the min distances and fixed positions +Vector +Spacing_problem::try_initial_solution() const +{ + int dim=cols.sz(); + Vector initsol(dim); + for (int i=0; i < dim; i++) { + if (cols[i].fixed) { + initsol(i)=cols[i].fixpos; + } else { + Real mindist=cols[i-1].minright() + +cols[i].minleft(); + assert(mindist >= 0.0); + initsol(i)=initsol(i-1)+mindist; + + //nog niet + //if (i>0) + // assert(initsol(i) > initsol(i-1)); + } + } + + return initsol; +} +Vector +Spacing_problem::find_initial_solution() const +{ + Vector v(try_initial_solution()); + assert(check_constraints(v)); + return v; +} +// generate the matrices +void +Spacing_problem::make_matrices(Matrix &quad, Vector &lin, Real &c) const +{ + quad.fill(0); + lin.fill(0); + for (int j=0; j < ideals.sz(); j++){ + Idealspacing const*i=ideals[j]; + int l = col_id(i->left); + int r = col_id(i->right); + + quad(r,r) += i->hooke; + quad(r,l) -= i->hooke; + quad(l,r) -= i->hooke; + quad(l,l) += i->hooke; + + lin(r) -= i->space*i->hooke; + lin(l) += i->space*i->hooke; + + c += sqr(i->space); + } +} + +// put the constraints into the LP problem +void +Spacing_problem::make_constraints(Mixed_qp& lp) const +{ + int dim=cols.sz(); + for (int j=0; j < dim; j++) { + Colinfo *c=&(cols[j]); + if (c->fixed) { + lp.add_fixed_var(j,c->fixpos); + } + 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()); + } + } +} + +svec<Real> +Spacing_problem::solve() const +{ + print(); + OK(); + assert(check_feasible()); + + + /* optimalisatiefunctie */ + Mixed_qp lp(cols.sz()); + 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" ; + } + + + svec<Real> posns(sol); + posns.add(lp.eval(sol)); + return posns; +} + +/* + add one column to the problem. +*/ +void +Spacing_problem::add_column(const PCol *col, bool fixed, Real fixpos) +{ + Colinfo c; + c.fixed=fixed; + c.fixpos=fixpos; + assert(col); + c.pcol_=col; + cols.add(c); +} + +void +Spacing_problem::add_ideal(const Idealspacing *i) +{ + const PCol *l =i->left; + const PCol *r= i->right; + + if (!contains(l) || !contains(r)) { + return; + } + ideals.add(i); +} + +void +Spacing_problem::print_ideal(const Idealspacing*id)const +{ +#ifndef NPRINT + int l = col_id(id->left); + int r = col_id(id->right); + + mtor << "between " << l <<","<<r<<":" ; +#endif +} + +void +Spacing_problem::print() const +{ +#ifndef NPRINT + for (int i=0; i < cols.sz(); i++) { + mtor << "col " << i<<' '; + cols[i].print(); + } + for (int i=0; i < ideals.sz(); i++) { + print_ideal(ideals[i]); + } +#endif + +} + +void +Colinfo::print() const +{ +#ifndef NPRINT + mtor << "column { "; + if (fixed) + mtor << "fixed at " << fixpos<<", "; + assert(pcol_); + mtor << "[" << minleft() << ", " << minright() << "]"; + mtor <<"}\n"; +#endif +} + +Colinfo::Colinfo() +{ + fixed=false; + pcol_=0; +} diff --git a/src/linestaff.cc b/src/linestaff.cc new file mode 100644 index 0000000000..a52954e6f4 --- /dev/null +++ b/src/linestaff.cc @@ -0,0 +1,20 @@ +#include "linestaff.hh" +#include "symbol.hh" +#include "lookup.hh" +#include "dimen.hh" +#include "paper.hh" +#include "pscore.hh" + +Linestaff::Linestaff(int l, PScore *s) + : PStaff(s) +{ + nolines = l; + stafsym = s->paper_->lookup_->linestaff(l); +} + +Symbol +Linestaff::get_stafsym(Real width)const +{ + String w(print_dimen(width)); + return stafsym->eval(w); +} diff --git a/src/lookup.cc b/src/lookup.cc new file mode 100644 index 0000000000..d0ce3cb743 --- /dev/null +++ b/src/lookup.cc @@ -0,0 +1,160 @@ +#include "lookup.hh" +#include "debug.hh" +#include "symtable.hh" +#include "dimen.hh" +#include "tex.hh" + +void +Lookup::parse(Text_db&t) +{ + symtables_->read(t) ; +} + +Lookup::Lookup() +{ + symtables_ = new Symtables; +} + +Lookup::~Lookup() +{ + delete symtables_; +} + +Symbol +Lookup::ball(int j) +{ + if (j > 4) + j = 4; + + Symtable * st = (*symtables_)("balls"); + return st->lookup(String(j)); +} + +Symbol +Lookup::rest(int j) +{ + return (*symtables_)("rests")->lookup(String(j)); +} + + +Symbol +Lookup::bar(String s) +{ + return (*symtables_)("bars")->lookup(s); +} + Symbol +Lookup::dots(int j) +{ + if (j>3) + error("max 3 dots"); + return (*symtables_)("dots")->lookup(j); +} + +Symbol +Lookup::flag(int j) +{ + return (*symtables_)("flags")->lookup(j); +} + +Symbol +Lookup::streepjes(int i) +{ + assert(i); + + int arg; + String idx ; + if (i<0) { + idx = "botlines"; + arg = -i; + }else { + arg = i; + idx = "toplines"; + } + Symbol ret = (*symtables_)("streepjes")->lookup(idx); + + svec<String> a; + a.add(arg); + ret.tex = substitute_args(ret.tex, a); + + return ret; +} + +/****************************************************************/ +// bare bones. + +struct Linestaf_symbol : Parametric_symbol { + int lines; + Linestaf_symbol(int n, Symtables*s): Parametric_symbol(s) { lines = n;} + Symbol eval(svec<String>)const; +}; + + +Symbol +Linestaf_symbol::eval(svec<String> w)const +{ + Real wid = w[0].fvalue(); + + Symbol s; + s.dim.x = Interval(0,wid); + Real dy=(lines-1)*convert_dimen(5,"pt"); // TODO! + s.dim.y = Interval(0,dy); + svec<String> a; + a.add(lines); + a.add(w[0]); + s.tex = (*symtables_)("param")->lookup("linestaf").tex; + s.tex = substitute_args(s.tex, a); + return s; +} + +/****************************************************************/ + + +struct Meter_sym:Parametric_symbol { + + Meter_sym(Symtables*s) : Parametric_symbol(s){ } + Symbol eval(svec<String> a) const{ + Symbol s; + s.dim.x = Interval( convert_dimen(-5,"pt"), + convert_dimen(5,"pt")); + s.dim.y = Interval(0, convert_dimen(10,"pt") ); // todo + String src = (*symtables_)("param")->lookup("meter").tex; + s.tex = substitute_args(src,a); + return s; + } +}; +/****************************************************************/ + +struct Stem_sym:Parametric_symbol { + + Stem_sym(Symtables*s) : Parametric_symbol(s) { } + Symbol eval(svec<String> a) const { + Real y1 = a[0].fvalue(); + Real y2 = a[1].fvalue(); + assert(y1 <= y2); + Symbol s; + s.dim.x = Interval(0,0); + s.dim.y = Interval(y1,y2); + + String src = (*symtables_)("param")->lookup("stem").tex; + s.tex = substitute_args(src,a); + return s; + } +}; + +Parametric_symbol * +Lookup::meter(String ) +{ + return new Meter_sym(symtables_); +} + +Parametric_symbol * +Lookup::linestaff(int n) +{ + return new Linestaf_symbol(n,symtables_); +} + +Parametric_symbol* +Lookup::stem() +{ + return new Stem_sym(symtables_); +} diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000000..c45de7a683 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,79 @@ +#include <iostream.h> +#include <assert.h> +#include "lgetopt.hh" +#include "misc.hh" +#include "string.hh" +#include "main.hh" + +extern void parse_file(String s); + +long_option_init theopts[] = { + 1, "output", 'o', + 0, "warranty", 'w', + 0, "help", 'h', + 0,0,0 +}; + +void +help() +{ + cout << + "--help, -h This help\n" + "--warranty, -w show warranty & copyright\n" + "--output, -o set default output\n"; +} +void notice() +{ + cout << + "LilyPond, a music typesetter.\n" + "Copyright (C) 1996 by\n" + " Han-Wen Nienhuys <hanwen@stack.urc.tue.nl>\n" + "\n" + " This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License version 2\n" + "as published by the Free Software Foundation.\n" + "\n" + " This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n" + "General Public License for more details.\n" + "\n" + " You should have received a copy (refer to the file COPYING) of the\n" + "GNU General Public License along with this program; if not, write to\n" + "the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,\n" + "USA.\n"; +} + +int +main (int argc, char **argv) +{ + Getopt_long oparser(argc, argv,theopts); + debug_init(); + cout << get_version(); + + while (long_option_init * opt = oparser()) { + switch ( opt->shortname){ + case 'o': + set_default_output(oparser.optarg); + break; + case 'w': + notice(); + exit(0); + break; + case 'h': + help(); + exit(0); + break; + default: + assert(false); + break; + } + } + char *arg = oparser.get_next_arg(); + + if (!arg) arg = ""; + parse_file(arg); + + do_scores(); + exit (0); +} diff --git a/src/melodicstaff.cc b/src/melodicstaff.cc new file mode 100644 index 0000000000..17916d2f22 --- /dev/null +++ b/src/melodicstaff.cc @@ -0,0 +1,59 @@ +#include "melodicstaff.hh" +#include "stem.hh" +#include "rest.hh" +#include "notehead.hh" +#include "paper.hh" +#include "molecule.hh" +#include "linestaff.hh" +#include "rhythmstaff.hh" +#include "sccol.hh" + +const int NO_LINES=5; +const int BOTTOM_POSITION=2; // e is on bottom line of 5-staff... + +void +Melodic_staff::set_output(PScore*ps) +{ + theline = new Linestaff(NO_LINES,ps); + Simple_staff::set_output(ps); +} + + +Notehead* +Melodic_staff::get_notehead(Note_req *rq) +{ + int b = rq->rhythmic()->balltype; + int d = rq->rhythmic()->dots; + + Notehead *n =new Notehead((NO_LINES-1)*2); + n->balltype =b; + n->dots = d; + n->position = rq->note()->height() - BOTTOM_POSITION; + return n; +} + + +Stem * +Melodic_staff::get_stem(Stem_req*rq) +{ + Stem * s = new Stem(NO_LINES-1); + s->flag = rq->stem_number; + return s; +} + +/* + creation + */ +Staff * +get_new_melodicstaff() +{ + return new Melodic_staff; +} + + + +Melodic_staff* +Melodic_staff::clone()const +{ + return new Melodic_staff(*this); +} diff --git a/src/meter.cc b/src/meter.cc new file mode 100644 index 0000000000..0afa8ea1a1 --- /dev/null +++ b/src/meter.cc @@ -0,0 +1,20 @@ +#include "string.hh" +#include "molecule.hh" +#include "meter.hh" +#include "paper.hh" +#include "lookup.hh" + + +Meter::Meter(svec<String> a) + :args(a) +{ +} +void +Meter::preprocess() +{ + Parametric_symbol *p = paper()->lookup_->meter("general"); + Symbol s = p->eval(args); + delete p; + output = new Molecule(Atom(s)); +} + diff --git a/src/misc.cc b/src/misc.cc new file mode 100644 index 0000000000..54746fd836 --- /dev/null +++ b/src/misc.cc @@ -0,0 +1,14 @@ +#include "misc.hh" +#include "glob.hh" + +#include <math.h> + +int intlog2(int d) { + int i=0; + while (!(d&1)) { + d/= 2; i++; + } + assert(!(d/2)); + return i; +} + diff --git a/src/molecule.cc b/src/molecule.cc new file mode 100644 index 0000000000..e8c4d2adec --- /dev/null +++ b/src/molecule.cc @@ -0,0 +1,144 @@ +#include "glob.hh" +#include "dimen.hh" +#include "string.hh" +#include "molecule.hh" +#include "symbol.hh" +#include "debug.hh" +#include "tex.hh" + +void +Atom::print() const +{ + mtor << "texstring: " <<sym.tex<<"\n"; +} + +Box +Atom::extent() const +{ + Box b( sym.dim); + b.translate(off); + return b; +} + +Atom::Atom(Symbol s) +{ + sym=s; +} + + +String +Atom::TeXstring() const +{ + // whugh.. Hard coded... + String s("\\placebox{%}{%}{%}"); + svec<String> a; + a.add(print_dimen(off.y)); + a.add(print_dimen(off.x)); + a.add(sym.tex); + return substitute_args(s, a); +} + + +String +Molecule::TeXstring() const +{ + String s; + for(PCursor<Atom*> c(ats); c.ok(); c++) + s+=c->TeXstring(); + return s; +} + +Box +Molecule::extent() const +{ + Box b; + for(PCursor<Atom*> c(ats); c.ok(); c++) + b.unite(c->extent()); + return b; +} + +void +Molecule::translate(Offset o) +{ + for (PCursor<Atom*> c(ats); c.ok(); c++) + c->translate(o); +} + +void +Molecule::add(const Molecule &m) +{ + for (PCursor<Atom*> c(m.ats); c.ok(); c++) { + add(**c); + } +} + +void +Molecule::add_right(const Molecule &m) +{ + if (!ats.size()) { + add(m); + return; + } + Real xof=extent().x.max - m.extent().x.min; + Molecule toadd(m); + toadd.translate(Offset(xof, 0.0)); + add(toadd); +} + +void +Molecule::add_left(const Molecule &m) +{ + if (!ats.size()) { + add(m); + return; + } + Real xof=extent().x.min - m.extent().x.max; + Molecule toadd(m); + toadd.translate(Offset(xof, 0.0)); + add(toadd); +} + + +void +Molecule::add_top(const Molecule &m) +{ + if (!ats.size()) { + add(m); + return; + } + Real yof=extent().y.max - m.extent().y.min; + Molecule toadd(m); + toadd.translate(Offset(0,yof)); + add(toadd); +} + +void +Molecule::add_bot(const Molecule &m) +{ + if (!ats.size()) { + add(m); + return; + } + Real yof=extent().y.min- m.extent().y.max; + Molecule toadd(m); + toadd.translate(Offset(0,yof)); + add(toadd); +} + +void +Molecule::operator = (const Molecule&) +{ + assert(false); +} + +Molecule::Molecule(const Molecule&s) +{ + add(s); +} + +void +Molecule::print() const +{ + for (PCursor<Atom*> c(ats); c.ok(); c++) + c->print(); +} diff --git a/src/note.cc b/src/note.cc new file mode 100644 index 0000000000..0041bb7238 --- /dev/null +++ b/src/note.cc @@ -0,0 +1,206 @@ +#include <ctype.h> + +#include "string.hh" +#include "real.hh" +#include "debug.hh" +#include "request.hh" +#include "voice.hh" +#include "notename.hh" +#include "vray.hh" + +int default_duration = 4, default_dots=0, default_octave=0; + +void +parse_duration(const char *a, int &j, int &intdur, int &dots) +{ + String durstr; + while (isdigit(a[j])) + { + durstr += a[j++]; + } + + dots=default_dots; + + while (a[j] == '.') + { + j++; + dots++; + } + + intdur = (durstr.len()) ? + durstr.value():default_duration; + + mtor << "dur " << intdur << "dots " << dots<<eol; +} + + + +void +parse_pitch( const char *a, int &j, int &oct, bool & overide_acc, + int & large, int & small) +{ + // octave + oct =default_octave; + + while (1) + { + if (a[j] == '\'') + oct ++; + else if (a[j] == '`') + oct --; + else + break; + j++; + + } + + mtor << "oct " << oct; + + // accidental + overide_acc = false; + + if (a[j] == '!') + { + overide_acc = true; + j++; + } + + + // notename. + String nm; + while (isalpha(a[j])) + { + nm += a[j++]; + } + if (isupper(nm[0])) + { + oct--; + nm.lower(); + } + + + lookup_notename(large,small,nm); + mtor << "override: " << overide_acc; + mtor << "pitch "<< large <<", "<<small<<"\n"; +} + + +Voice_element * +get_note_element(String pitch, String durstr) +{ + Voice_element*v = new Voice_element; + int i=0; + + int dur, dots; + parse_duration(durstr, i, dur, dots); + i=0; + + Note_req * rq = new Note_req; + + if (dur >= 2) { + Stem_req * st = new Stem_req(dur); + v->add(st); + } + + int oct, pit, acc; + bool forceacc; + parse_pitch(pitch, i, oct, forceacc, pit, acc); + char nm = pit + 'c'; + if (nm > 'g') + nm += 'a' - 'h'; + rq->name =nm; + + rq->octave = oct; + rq->accidental = acc; + rq->forceacc = forceacc; + rq->balltype = dur; + rq->dots = dots; + + rq->print(); + + v->add(rq); + + return v; +} + +Voice_element * +get_rest_element(String, String durstr) +{ + Voice_element*v = new Voice_element; + int i=0; + + int dur, dots; + parse_duration(durstr, i, dur, dots); + i=0; + + Rest_req * rq = new Rest_req; + + rq->balltype = dur; + rq->dots = dots; + rq->print(); + v->add(rq); + + return v; +} + +void +set_default_duration(String d) +{ + int i=0; + parse_duration(d, i, default_duration, default_dots); +} + + +void +set_default_pitch(String d) +{ + int i=0; + bool b; + int l,s; + parse_pitch(d, i, default_octave, b, l,s); +} + +Request* +get_request(char c) +{ + Request* ret=0; + switch (c) { + case '[': + case ']': + ret = new Beam_req; + break; + + case ')': + case '(': + ret = new Slur_req; + break; + default: + assert(false); + break; + } + + switch (c) { + case '(': + case '[': + ret->span()->spantype = Span_req::START; + break; + case ')': + case ']': + ret->span()->spantype = Span_req::STOP; + break; + default: + assert(false); + break; + } + + return ret; +} + +void +add_requests(Voice_element *v, svec<Request*> &req) +{ + for (int i = 0; i < req.sz(); i++) { + v->add(req[i]); + } + req.set_size(0); +} diff --git a/src/notehead.cc b/src/notehead.cc new file mode 100644 index 0000000000..a103e69b3d --- /dev/null +++ b/src/notehead.cc @@ -0,0 +1,65 @@ +#include "notehead.hh" +#include "dimen.hh" +#include "debug.hh" +#include "paper.hh" +#include "lookup.hh" +#include "molecule.hh" + + +Notehead::Notehead(int ss) +{ + staff_size=ss; + position = 0; + balltype = 0; + dots = 0; +} + +void +Notehead::print()const +{ + mtor << "Head "<<balltype<<", position = "<< position << "dots " << dots; + Item::print(); +} + +void +Notehead::preprocess() +{ + brew_molecole(); +} + +void +Notehead::brew_molecole() +{ + assert(pstaff_); + assert(!output); + + Paperdef *p = paper(); + + Real dy = p->interline()/2; + Symbol s = p->lookup_->ball(balltype); + + output = new Molecule(Atom(s)); + if (dots) { + Symbol d = p->lookup_->dots(dots); + Molecule dm; + dm.add(Atom(d)); + if (!(position %2)) + dm.translate(Offset(0,dy)); + output->add_right(dm); + } + bool streepjes = (position<-1)||(position > staff_size+1); + if (streepjes) { + int dir = sgn(position); + int s =(position<-1) ? -((-position)/2): (position-staff_size)/2; + Symbol str = p->lookup_->streepjes(s); + Molecule sm; + sm.add(Atom(str)); + if (position % 2) + sm.translate(Offset(0,-dy* dir)); + output->add(sm); + } + + + output->translate(Offset(0,dy*position)); +} + diff --git a/src/notename.cc b/src/notename.cc new file mode 100644 index 0000000000..8ebc4df244 --- /dev/null +++ b/src/notename.cc @@ -0,0 +1,30 @@ +#include "glob.hh" +#include "string.hh" + + +/// change this along with lex file for other notenames. +const char *notetab[] = +{ +"ceses", "ces", "c", "cis", "cisis", +"deses", "des", "d", "dis", "disis", +"eses", "es", "e", "eis", "eisis", +"feses", "fes", "f", "fis", "fisis", +"geses", "ges", "g", "gis", "gisis", +"ases", "as", "a", "ais", "aisis", +"beses", "bes", "b", "bis", "bisis", +0 +}; + +void +lookup_notename(int &large, int &small, String s) +{ + int i; + for (i =0; notetab[i]; i++) + if (s == notetab[i]) + { + large = i /5; + small = i %5 - 2; + return; + } + assert(false); +} diff --git a/src/paper.cc b/src/paper.cc new file mode 100644 index 0000000000..8cd98339d4 --- /dev/null +++ b/src/paper.cc @@ -0,0 +1,82 @@ +#include <math.h> + +#include "paper.hh" +#include "debug.hh" +#include "lookup.hh" +#include "dimen.hh" +#include "textdb.hh" + +double log2(double x) { + return log(x) /log(2.0); +} + + +// golden ratio +const Real PHI = (1+sqrt(5))/2; + +// see Roelofs, p. 57 +Real +Paperdef::duration_to_dist(Real d) +{ + return whole_width * pow(geometric_, log2(d)); +} +Real +Paperdef::rule_thickness()const +{ + return convert_dimen(0.4, "pt"); +} +Paperdef::Paperdef() +{ + lookup_ = new Lookup(); + parse(); + linewidth = convert_dimen(15,"cm"); // in cm for now + whole_width= 8*note_width(); + geometric_ = PHI; +} + +void +Paperdef::parse() +{ + Text_db symini("symbol.ini"); + + + while (!symini.eof()) { + + Text_record r( symini++); + + if (r[0] == "symboltables") + lookup_->parse(symini); + } +} + +Paperdef::~Paperdef() +{ + delete lookup_; +} + +Real +Paperdef::interline() const +{ + return lookup_->ball(4).dim.y.length(); +} +Real +Paperdef::note_width()const +{ + return lookup_->ball(4).dim.x.length( ); +} +Real +Paperdef::standard_height() const +{ + return convert_dimen(20,"pt"); +} + +void +Paperdef::print() const +{ +#ifndef NPRINT + mtor << "Paper {width: " << print_dimen(linewidth); + mtor << "whole: " << print_dimen(whole_width); + mtor << "out: " <<outfile; + mtor << "}\n"; +#endif +} diff --git a/src/parser.y b/src/parser.y new file mode 100644 index 0000000000..691d306e78 --- /dev/null +++ b/src/parser.y @@ -0,0 +1,292 @@ +%{ // -*-Fundamental-*- +#include <iostream.h> + +#include "lexer.hh" +#include "paper.hh" +#include "staff.hh" +#include "score.hh" +#include "main.hh" +#include "keyword.hh" +#include "scommands.hh" +#include "debug.hh" +#include "parseconstruct.hh" +#include "dimen.hh" +#include "identifier.hh" + +#ifndef NDEBUG +#define YYDEBUG 1 +#endif + +svec<Request*> pre_reqs, post_reqs; +%} + + +%union { + Real real; + Command *command; + Identifier *id; + Score_commands *scommands; + Voice *voice; + Voice_element *el; + Staff *staff; + String *string; + Score *score; + const char *consstr; + Paperdef *paper; + Request* request; + int i; + char c; +} + +%token VOICE STAFF SCORE TITLE RHYTHMSTAFF BAR NOTENAME OUTPUT +%token CM IN PT MM PAPER WIDTH METER UNITSPACE SKIP COMMANDS +%token MELODICSTAFF GEOMETRIC START_T DURATIONCOMMAND OCTAVECOMMAND + +%token <id> IDENTIFIER +%token <string> NEWIDENTIFIER +%token <string> PITCH DURATION RESTNAME +%token <real> REAL +%token <string> STRING +%token <i> OPEN_REQUEST_PARENS CLOSE_REQUEST_PARENS + + +%type <consstr> unit + +%type <id> declaration +%type <paper> paper_block paper_body +%type <real> dim +%type <voice> voice_block voice_body voice_elts voice_elts_dollar +%type <el> voice_elt +%type <command> score_command +%type <score> score_block score_body +%type <staff> staff_block rhythmstaff_block rhythmstaff_body +%type <staff> melodicstaff_block melodicstaff_body staffdecl +%type <i> int +%type <scommands> score_commands_block score_commands_body +%type <request> post_request pre_request + + +%% + +mudela: /* empty */ + | mudela score_block { + add_score($2); + } + | mudela add_declaration { } + ; + +add_declaration: declaration { + add_identifier($1); + } + ; + +declaration: + NEWIDENTIFIER '=' staff_block { + $$ = new Staff_id(*$1, $3); + delete $1; // this sux + } + | NEWIDENTIFIER '=' voice_block { + $$ = new Voice_id(*$1, $3); + delete $1; + } + ; + + +score_block: SCORE '{' score_body '}' { $$ = $3; } + ; + +score_body: { $$ = new Score; } + | score_body staff_block { $$->add($2); } + | score_body score_commands_block { $$->set($2); } + | score_body paper_block { $$->set($2); } + ; +score_commands_block: + COMMANDS '{' score_commands_body '}' { $$ =$3;} + ; + +score_commands_body: { $$ = new Score_commands; } + | score_commands_body score_command { + $$->parser_add($2); + } + ; + +paper_block: + PAPER '{' paper_body '}' { $$ = $3; } + ; + +paper_body: + /* empty */ { $$ = new Paperdef; } + | paper_body WIDTH dim { $$->linewidth = $3;} + | paper_body OUTPUT STRING { $$->outfile = *$3; + delete $3; + } + | paper_body UNITSPACE dim { $$->whole_width = $3; } + | paper_body GEOMETRIC REAL { $$->geometric_ = $3; } + ; + +dim: + REAL unit { $$ = convert_dimen($1,$2); } + ; + + +unit: CM { $$ = "cm"; } + |IN { $$ = "in"; } + |MM { $$ = "mm"; } + |PT { $$ = "pt"; } + ; + +/* + staff +*/ +staff_block: + staffdecl + | rhythmstaff_block + | melodicstaff_block + ; + +staffdecl: STAFF '{' IDENTIFIER '}' { $$ = $3->staff()->clone(); } + ; + +rhythmstaff_block: + RHYTHMSTAFF '{' rhythmstaff_body '}' { $$ = $3; } + ; + +rhythmstaff_body: + /* empty */ { $$ = get_new_rhythmstaff(); } + | rhythmstaff_body voice_block { $$->add_voice($2); } + ; + +melodicstaff_block: + MELODICSTAFF '{' melodicstaff_body '}' { $$ = $3; } + ; + +melodicstaff_body: + /* empty */ { $$ = get_new_melodicstaff(); } + | melodicstaff_body voice_block { $$->add_voice($2); } + ; + +/* + voice +*/ +voice_block: + VOICE '{' voice_body '}' { $$ = $3; } + ; + + +voice_body: + IDENTIFIER { $$ = new Voice(*$1->voice()); } + | voice_elts_dollar { $$ = $1; } + | voice_body START_T REAL { $$->start = $3; } + ; + + + + +voice_elts_dollar: + '$' voice_elts '$' { $$ = $2; } + ; + +voice_elts: + /* empty */ { + $$ = new Voice; + } + | voice_elts pre_requests voice_elt post_requests { + add_requests($3, pre_reqs); + add_requests($3, post_reqs); + $$->add($3); + } + + | voice_elts voice_command { } + ; + +post_requests: + { + assert(post_reqs.empty()); + } + | post_requests post_request { + post_reqs.add($2); + } + ; + +post_request: + CLOSE_REQUEST_PARENS { $$ = get_request($1); } + ; + +pre_requests: + | pre_requests pre_request { + pre_reqs.add($2); + } + ; + +pre_request: + OPEN_REQUEST_PARENS { $$ = get_request($1); } + ; +/* +*/ +voice_command: + DURATIONCOMMAND DURATION { + set_default_duration(*$2); + delete $2; + } + | OCTAVECOMMAND PITCH { + set_default_pitch(*$2); + delete $2; + } + ; + +voice_elt: + PITCH DURATION { + $$ = get_note_element(*$1, *$2); + delete $1; + delete $2; + } + | RESTNAME DURATION { + $$ = get_rest_element(*$1, *$2); + delete $1; + delete $2; + } + | PITCH { $$ = get_note_element(*$1, ""); + delete $1; + } + | RESTNAME { $$ = get_rest_element(*$1, ""); + delete $1; + } + ; + +score_command: + SKIP int ':' REAL { + $$ = get_skip_command($2, $4); + } + | METER int int { + $$ = get_meterchange_command($2, $3); + } +/* | PARTIALMEASURE REAL { + $$ = get_partial_command($2); + }*/ + ; + + +int: + REAL { + $$ = int($1); + if (ABS($1-Real(int($$))) > 1e-8) + yyerror("expecting integer number"); + + } + ; + +%% + +void +parse_file(String s) +{ + *mlog << "Parsing ... "; +#ifdef YYDEBUG + yydebug = !monitor.silence("Parser"); +#endif + new_input(s); + yyparse(); + delete_identifiers(); + kill_lexer(); + *mlog << "\n"; +} diff --git a/src/pcol.cc b/src/pcol.cc new file mode 100644 index 0000000000..902f57bab2 --- /dev/null +++ b/src/pcol.cc @@ -0,0 +1,118 @@ +#include "pcol.hh" +#include "pstaff.hh" +#include "debug.hh" + +void +Idealspacing::print() const +{ +#ifndef NPRINT + mtor << "idealspacing {" ; + mtor << "distance "<<space<< " strength " << hooke << "}\n"; +#endif +} + +Idealspacing::Idealspacing(const PCol * l,const PCol * r) +{ + space = 0.0; + hooke = 0.0; + left = l; + right = r; +} + +void +Idealspacing::OK() const +{ +#ifndef NDEBUG + assert(hooke >= 0 && left && right); +#endif +} + +/****************************************************************/ + +Interval +PCol::width() const +{ + Interval w; + + for (PCursor<const Item *> ic(its); ic.ok(); ic++) + w.unite(ic->width()); + if (w.empty()) + w.unite(Interval(0,0)); + return w; +} + +void +PCol::print() const +{ + #ifndef NPRINT + mtor << "PCol {"; + mtor << "# symbols: " << its.size() ; + if (breakable()){ + mtor << "pre,post: "; + prebreak->print(); + postbreak->print(); + } + mtor << "extent: " << width().min << ", " << width().max << "\n"; + mtor << "}\n"; + #endif +} + +int +PCol::compare(const PCol &, const PCol &) +{ + assert(false); + return 0 ; +} + +void +PCol::OK () const +{ + if (prebreak || postbreak ) { + assert(prebreak&&postbreak); + assert(prebreak->daddy == this); + assert(postbreak->daddy == this); + } + +} + +void +PCol::set_breakable() +{ + if (breakable()) + return; + + prebreak = new PCol(this); + postbreak = new PCol(this); + used = true; +} + +bool +PCol::breakable() const +{ + return prebreak||postbreak; +} + +PCol::PCol(PCol *parent) { + daddy = parent; + prebreak=0; + postbreak=0; + line=0; + used = false; +} + +PCol::~PCol() +{ + + delete prebreak; + + delete postbreak; +} + +void +PCol::add( Item *i) +{ + its.bottom().add(i); + i->pcol_ = this; + used = true; +} + diff --git a/src/pscore.cc b/src/pscore.cc new file mode 100644 index 0000000000..8d7067760f --- /dev/null +++ b/src/pscore.cc @@ -0,0 +1,205 @@ +// utility functions for PScore +#include "debug.hh" +#include "spanner.hh" +#include "paper.hh" +#include "molecule.hh" +#include "dimen.hh" +#include "scoreline.hh" +#include "pscore.hh" +#include "tstream.hh" + + +void +PScore::clean_cols() +{ + for (PCursor<PCol *> c(cols); c.ok(); ) + if (!c->used) { + c.del(); + } else + c++; +} + + +void +PScore::add(PStaff *s) +{ + assert(s->pscore_ == this); + staffs.bottom().add(s); +} + +void +PScore::typeset_item(Item *i, PCol *c, PStaff *s, int breakstat) +{ + assert(c && i && s); +// assert(!breakstat != 4 || c->breakable() ); + if (breakstat == 0) { + typeset_item(i, c->prebreak, s); + return; + } + + if (breakstat == 2) { + typeset_item(i, c->postbreak, s); + return; + } + + + its.bottom().add(i); + s->add(i); + c->add(i); + + /* first do this, because i->width() may follow the 0-pointer */ + i->preprocess(); + + + if (c->daddy && c == c->daddy->prebreak) { // makeshift. + + Interval iv (i->width()); + if (!iv.empty()) { + svec<Item*> col_its (select_items(s, c)); + for (int j =0; j < col_its.sz(); j++) + col_its[j]->translate(Offset(-iv.length(),0)); + i->translate (Offset(-iv.max, 0)); + } + } + +} + +void +PScore::typeset_spanner(Spanner*sp, PStaff*ps) +{ + sp->preprocess(); + sp->pstaff_ = ps; + spanners.bottom().add(sp); + ps->spans.bottom().add(sp); + sp->left->starters.bottom().add(sp); + sp->right->stoppers.bottom().add(sp); +} + + +void +PScore::add_line(svec<const PCol *> curline, svec<Real> config) +{ + Line_of_score *p = new Line_of_score(curline,this); + lines.bottom().add(p); + for (int i=0; i < curline.sz(); i++){ + PCol *c=(PCol *)curline[i]; // so, this isn't really const. + c->hpos= config[i]; + } +} + +Idealspacing* +PScore::get_spacing(PCol*l, PCol*r) +{ + assert(l!=r); + for (PCursor<Idealspacing*> ic (suz); ic.ok(); ic++) { + if (ic->left == l && ic->right == r){ + return ic; + } + } + + Idealspacing*ip =new Idealspacing(l,r); + suz.bottom().add(ip); + + return ip; +} + + +/* + return all breakable columns + */ +svec<const PCol *> +PScore::find_breaks() const +{ + svec<const PCol *> retval; + for (PCursor<PCol *> c(cols); c.ok(); c++) + if (c->breakable()) + retval.add(c); + + return retval; +} + +void +PScore::add(PCol *p) +{ + cols.bottom().add(p); +} + +PScore::PScore( Paperdef*p) +{ + paper_ = p; +} + +void +PScore::output(Tex_stream &ts) +{ + int l=1; + + for (PCursor<Line_of_score*> lic(lines); lic.ok(); lic++) { + ts << "% line of score no. " << l++ <<"\n"; + ts << lic->TeXstring(); + if ((lic+1).ok()) + ts << "\\interscoreline\n"; + } +} + + +svec<Item*> +PScore::select_items(PStaff*ps , PCol*pc) +{ + svec<Item*> ret; + assert(ps && pc); + for (PCursor<const Item*> ic(pc->its); ic.ok(); ic++){ + if (ic->pstaff_ == ps) + ret.add((Item*)(const Item*)ic); + } + return ret; +} + +void +PScore::OK()const +{ +#ifdef NDEBUG + for (PCursor<PCol*> cc(cols); cc.ok(); cc++) + cc->OK(); + for (PCursor<Idealspacing*> ic(suz); ic.ok(); ic++) + ic->OK(); +#endif +} + +void +PScore::print() const +{ +#ifndef NPRINT + mtor << "PScore { "; + paper_->print(); + mtor << "\ncolumns: "; + for (PCursor<PCol*> cc(cols); cc.ok(); cc++) + cc->print(); + + mtor << "\nideals: "; + for (PCursor<Idealspacing*> ic(suz); ic.ok(); ic++) + ic->print(); + mtor << "}\n"; +#endif +} + +void +PScore::preprocess() +{ +#if 0 + for (PCursor<Item*> ic(its); ic.ok(); ic++){ + ic->preprocess(); + } +#endif +} + +void +PScore::postprocess() +{ + for (PCursor<Spanner*> ic(spanners); ic.ok(); ic++) { + ic->process(); + } + for (PCursor<Item*> ic(its); ic.ok(); ic++){ + ic->postprocess(); + } +} diff --git a/src/pstaff.cc b/src/pstaff.cc new file mode 100644 index 0000000000..255fceeadd --- /dev/null +++ b/src/pstaff.cc @@ -0,0 +1,14 @@ +#include "pstaff.hh" + +PStaff::PStaff(PScore*ps) +{ + pscore_=ps; + stafsym = 0; +} + +void +PStaff::add(Item *i ) +{ + its.bottom().add(i); + i->pstaff_ = this; +} diff --git a/src/qlp.cc b/src/qlp.cc new file mode 100644 index 0000000000..3dd8e4227e --- /dev/null +++ b/src/qlp.cc @@ -0,0 +1,147 @@ +#include "debug.hh" +#include "const.hh" +#include "qlp.hh" +#include "choleski.hh" + +void +Mixed_qp::add_equality_cons(Vector , double ) +{ + assert(false); +} + +void +Mixed_qp::add_fixed_var(int i, Real r) +{ + eq_cons.add(i); + eq_consrhs.add(r); +} + +void +Ineq_constrained_qp::add_inequality_cons(Vector c, double r) +{ + cons.add(c); + consrhs.add(r); +} + +Ineq_constrained_qp::Ineq_constrained_qp(int novars): + quad(novars), + lin(novars) +{ +} + +void +Ineq_constrained_qp::OK() const +{ +#ifndef NDEBUG + assert(cons.sz() == consrhs.sz()); + Matrix Qdif= quad - quad.transposed(); + assert(Qdif.norm()/quad.norm() < EPS); +#endif +} + + +Real +Ineq_constrained_qp::eval (Vector v) +{ + return v * quad * v + lin * v + const_term; +} +/* + eliminate appropriate variables, until we have a Ineq_constrained_qp + then solve that. + + PRE + cons should be ascending + */ +Vector +Mixed_qp::solve(Vector start) const +{ + print(); + Ineq_constrained_qp pure(*this); + + for (int i= eq_cons.sz()-1; i>=0; i--) { + pure.eliminate_var(eq_cons[i], eq_consrhs[i]); + start.del(eq_cons[i]); + } + Vector sol = pure.solve(start); + for (int i= 0; i < eq_cons.sz(); i++) { + sol.insert( eq_consrhs[i],eq_cons[i]); + } + return sol; +} + +/* + assume x(idx) == value, and adjust constraints, lin and quad accordingly + */ +void +Ineq_constrained_qp::eliminate_var(int idx, Real value) +{ + Vector row(quad.row(idx)); + row*= value; + + quad.delete_row(idx); + + quad.delete_column(idx); + + lin.del(idx); + row.del(idx); + lin +=row ; + + for (int i=0; i < cons.sz(); i++) { + consrhs[i] -= cons[i](idx) *value; + cons[i].del(idx); + } +} + + + + +Mixed_qp::Mixed_qp(int n) + : Ineq_constrained_qp(n) +{ +} + +void +Mixed_qp::OK() const +{ +#ifndef NDEBUG + Ineq_constrained_qp::OK(); + assert(eq_consrhs.sz() == eq_cons.sz()); +#endif +} +void +Ineq_constrained_qp::print() const +{ +#ifndef NPRINT + mtor << "Quad " << quad; + mtor << "lin " << lin <<"\n"; + for (int i=0; i < cons.sz(); i++) { + mtor << "constraint["<<i<<"]: " << cons[i] << " >= " << consrhs[i]; + mtor << "\n"; + } +#endif +} +void +Mixed_qp::print() const +{ +#ifndef NPRINT + Ineq_constrained_qp::print(); + for (int i=0; i < eq_cons.sz(); i++) { + mtor << "eq cons "<<i<<": x["<<eq_cons[i]<<"] == " << eq_consrhs[i]<<"\n"; + } +#endif +} + + +void +Ineq_constrained_qp::assert_solution(Vector sol) const +{ + svec<int> binding; + for (int i=0; i < cons.sz(); i++) { + Real R=cons[i] * sol- consrhs[i]; + assert(R> -EPS); + if (R < EPS) + binding.add(i); + } + // KKT check... + // todo +} diff --git a/src/qlpsolve.cc b/src/qlpsolve.cc new file mode 100644 index 0000000000..13bd30e89b --- /dev/null +++ b/src/qlpsolve.cc @@ -0,0 +1,255 @@ +#include "qlpsolve.hh" +#include "const.hh" +#include "debug.hh" +#include "choleski.hh" + +const Real TOL=1e-2; // roughly 1/10 mm + +String +Active_constraints::status() const +{ + String s("Active|Inactive ["); + for (int i=0; i< active.sz(); i++) { + s += String(active[i]) + " "; + } + + s+="| "; + for (int i=0; i< inactive.sz(); i++) { + s += String(inactive[i]) + " "; + } + s+="]"; + + return s; +} + +void +Active_constraints::OK() { + H.OK(); + A.OK(); + assert(active.sz() +inactive.sz() == opt->cons.sz()); + assert(H.dim() == opt->dim()); + assert(active.sz() == A.rows()); + svec<int> allcons; + + for (int i=0; i < opt->cons.sz(); i++) + allcons.add(0); + for (int i=0; i < active.sz(); i++) { + int j = active[i]; + allcons[j]++; + } + for (int i=0; i < inactive.sz(); i++) { + int j = inactive[i]; + allcons[j]++; + } + for (int i=0; i < allcons.sz(); i++) + assert(allcons[i] == 1); +} + +Vector +Active_constraints::get_lagrange(Vector gradient) +{ + Vector l(A*gradient); + + return l; +} + +void +Active_constraints::add(int k) +{ + // add indices + int cidx=inactive[k]; + active.add(cidx); + + inactive.swap(k,inactive.sz()-1); + inactive.pop(); + + Vector a( opt->cons[cidx] ); + // update of matrices + Vector Ha = H*a; + Real aHa = a*Ha; + if (ABS(aHa) > EPS) { + /* + a != 0, so if Ha = O(EPS), then + Ha * aH / aHa = O(EPS^2/EPS) + + if H*a == 0, the constraints are dependent. + */ + H -= Matrix(Ha , Ha)/(aHa); + + + /* + sorry, don't know how to justify this. .. + */ + Vector addrow(Ha/(aHa)); + A -= Matrix(A*a, addrow); + A.insert_row(addrow,A.rows()); + }else + WARN << "degenerate constraints"; +} + +void +Active_constraints::drop(int k) +{ + int q=active.sz()-1; + + // drop indices + inactive.add(active[k]); + active.swap(k,q); + A.swap_rows(k,q); + active.pop(); + + Vector a(A.row(q)); + if (a.norm() > EPS) { + /* + + */ + H += Matrix(a,a)/(a*opt->quad*a); + A -= A*opt->quad*Matrix(a,a)/(a*opt->quad*a); + }else + WARN << "degenerate constraints"; + Vector rem_row(A.row(q)); + assert(rem_row.norm() < EPS); + A.delete_row(q); +} + + +Active_constraints::Active_constraints(Ineq_constrained_qp const *op) + : A(0,op->dim()), + H(op->dim()), + opt(op) +{ + for (int i=0; i < op->cons.sz(); i++) + inactive.add(i); + Choleski_decomposition chol(op->quad); + H=chol.inverse(); +} + +/* Find the optimum which is in the planes generated by the active + constraints. + */ +Vector +Active_constraints::find_active_optimum(Vector g) +{ + return H*g; +} + +/****************************************************************/ + +int +min_elt_index(Vector v) +{ + Real m=INFTY; int idx=-1; + for (int i = 0; i < v.dim(); i++){ + if (v(i) < m) { + idx = i; + m = v(i); + } + assert(v(i) <= INFTY); + } + return idx; +} + +///the numerical solving +Vector +Ineq_constrained_qp::solve(Vector start) const +{ + Active_constraints act(this); + + + act.OK(); + + + Vector x(start); + Vector gradient=quad*x+lin; + + + Vector last_gradient(gradient); + int iterations=0; + + while (iterations++ < MAXITER) { + Vector direction= - act.find_active_optimum(gradient); + + mtor << "gradient "<< gradient<< "\ndirection " << direction<<"\n"; + + if (direction.norm() > EPS) { + mtor << act.status() << '\n'; + + Real minalf = INFTY; + + Inactive_iter minidx(act); + + + /* + we know the optimum on this "hyperplane". Check if we + bump into the edges of the simplex + */ + + for (Inactive_iter ia(act); ia.ok(); ia++) { + + if (ia.vec() * direction >= 0) + continue; + Real alfa= - (ia.vec()*x - ia.rhs())/ + (ia.vec()*direction); + + if (minalf > alfa) { + minidx = ia; + minalf = alfa; + } + } + Real unbounded_alfa = 1.0; + Real optimal_step = MIN(minalf, unbounded_alfa); + + Vector deltax=direction * optimal_step; + x += deltax; + gradient += optimal_step * (quad * deltax); + + mtor << "step = " << optimal_step<< " (|dx| = " << + deltax.norm() << ")\n"; + + if (minalf < unbounded_alfa) { + /* bumped into an edge. try again, in smaller space. */ + act.add(minidx.idx()); + mtor << "adding cons "<< minidx.idx()<<'\n'; + continue; + } + /*ASSERT: we are at optimal solution for this "plane"*/ + + + } + + Vector lagrange_mult=act.get_lagrange(gradient); + int m= min_elt_index(lagrange_mult); + + if (m>=0 && lagrange_mult(m) > 0) { + break; // optimal sol. + } else if (m<0) { + assert(gradient.norm() < EPS) ; + + break; + } + + mtor << "dropping cons " << m<<'\n'; + act.drop(m); + } + if (iterations >= MAXITER) + WARN<<"didn't converge!\n"; + + mtor << ": found " << x<<" in " << iterations <<" iterations\n"; + assert_solution(x); + return x; +} + +/** Mordecai Avriel, Nonlinear Programming: analysis and methods (1976) + Prentice Hall. + + Section 13.3 + + This is a "projected gradient" algorithm. Starting from a point x + the next point is found in a direction determined by projecting + the gradient onto the active constraints. (well, not really the + gradient. The optimal solution obeying the active constraints is + tried. This is why H = Q^-1 in initialisation) ) + + + */ + diff --git a/src/request.cc b/src/request.cc new file mode 100644 index 0000000000..9928f62bed --- /dev/null +++ b/src/request.cc @@ -0,0 +1,101 @@ +#include "request.hh" +#include "debug.hh" + +#define VIRTUALCONS(T,R) R *T::clone() const { return new T(*this); } struct T +#define RCONS(T) VIRTUALCONS(T, Request) + +RCONS(Rest_req); +RCONS(Rhythmic_req); +RCONS(Stem_req); +RCONS(Note_req); +RCONS(Span_req); +RCONS(Slur_req); +RCONS(Beam_req); + +void +Request::print() const +{ +#ifndef NPRINT + mtor << "Req{ unknown }\n"; +#endif +} + +Request::Request() +{ + elt = 0; +} + +Note_req::Note_req() +{ + name = 'c'; + octave = 0; + accidental = 0; + forceacc = false; +} + +int +Note_req::height() const +{ + int s = name -'c'; + if (s < 0) + s+=7; + return s + octave*7; +} + +Rhythmic_req::Rhythmic_req() +{ + balltype = 1; + dots = 0; +} + +void +Rhythmic_req::print() const +{ + mtor << "rhythmic: " << balltype ; + int d =dots; + while (d--) + mtor << '.'; + mtor<<"\n"; +} + +void +Note_req::print() const +{ + mtor << "note: " << name << " oct: "<< octave; + Rhythmic_req::print(); +} + +void +Rest_req::print() const +{ + mtor << "rest, " ; + Rhythmic_req::print(); +} + +Real +wholes(int dur, int dots) +{ + Real f = 1.0/Real(dur); + Real delta = f; + + while (dots--) { + delta /= 2.0; + f += delta; + } + return f; +} + +Real +Rhythmic_req::duration() const { + return wholes( balltype,dots); +} + +Beam_req::Beam_req() +{ + nplet = 0; +} + +Span_req::Span_req() +{ + spantype = NOSPAN; +} diff --git a/src/rest.cc b/src/rest.cc new file mode 100644 index 0000000000..e448df37bd --- /dev/null +++ b/src/rest.cc @@ -0,0 +1,47 @@ +#include "rest.hh" +#include "dimen.hh" +#include "debug.hh" +#include "paper.hh" +#include "lookup.hh" +#include "molecule.hh" + + +Rest::Rest(int t, int d) +{ + balltype = t; + dots = d; +} + +void +Rest::print()const +{ + mtor << "Rest "<<balltype<< "dots " << dots; + Item::print(); +} + +void +Rest::preprocess() +{ + brew_molecole(); +} + +void +Rest::brew_molecole() +{ + assert(pstaff_); + assert(!output); + Paperdef *p =paper(); + + Symbol s; + s = p->lookup_->rest(balltype); + + Molecule *m = new Molecule(Atom(s)); + if (dots) { + Symbol d =p->lookup_->dots(dots); + Molecule dm; + dm.add(Atom(d)); + m->add_right(dm); + } + output = m; +} + diff --git a/src/rhythmstaff.cc b/src/rhythmstaff.cc new file mode 100644 index 0000000000..e584254bfd --- /dev/null +++ b/src/rhythmstaff.cc @@ -0,0 +1,62 @@ +#include "molecule.hh" +#include "notehead.hh" +#include "stem.hh" +#include "linestaff.hh" +#include "rhythmstaff.hh" +#include "paper.hh" +#include "sccol.hh" +#include "rest.hh" + +void +Rhythmic_staff::set_output(PScore*ps) +{ + theline = new Linestaff(1,ps); + Simple_staff::set_output(ps); +} + +Item * +Rhythmic_staff::get_TYPESET_item(Command *com) +{ + Item *i = Simple_staff::get_TYPESET_item(com); + i->translate(Offset(0, + -score_->paper_->standard_height()/2)); + return i; +} + +Notehead* +Rhythmic_staff::get_notehead(Note_req *rq) +{ + int b = rq->rhythmic()->balltype; + int d = rq->rhythmic()->dots; + + Notehead *n =new Notehead(1); + n->balltype = b; + n->dots =d; + n->position = 0; + return n; +} + +Stem * +Rhythmic_staff::get_stem(Stem_req*rq) +{ + Stem * s = new Stem(0); + s->flag = rq->stem_number; + return s; +} + +/* + creation + */ +Staff * +get_new_rhythmstaff() +{ + return new Rhythmic_staff; +} + + + +Rhythmic_staff* +Rhythmic_staff::clone() const +{ + return new Rhythmic_staff(*this); +} diff --git a/src/sccol.cc b/src/sccol.cc new file mode 100644 index 0000000000..1dfc8f2176 --- /dev/null +++ b/src/sccol.cc @@ -0,0 +1,28 @@ +#include "sccol.hh" +#include "debug.hh" + +Score_column::Score_column(Real w) +{ + when = w; + pcol = new PCol(0); + musical = false; +} + +bool +Score_column::used() { + return pcol->used; +} + +void +Score_column::print() const +{ +#ifndef NPRINT + mtor << "Score_column { mus "<< musical <<" at " << when<<'\n'; + mtor << "durations: ["; + for (int i=0; i < durations.sz(); i++) + mtor << durations[i] << " "; + mtor << "]\n"; + pcol->print(); + mtor << "}\n"; +#endif +} diff --git a/src/scommands.cc b/src/scommands.cc new file mode 100644 index 0000000000..7b8c8ed9c5 --- /dev/null +++ b/src/scommands.cc @@ -0,0 +1,266 @@ +#include "scommands.hh" +#include "debug.hh" +#include "parseconstruct.hh" + +/* + maybe it's time for a "narrowing" cursor? + */ +PCursor<Command*> +Score_commands::first(Real w) +{ + PCursor<Command*> pc(*this); + while (pc.ok() && pc->when < w) + pc++; + + return pc; +} + +PCursor<Command*> +Score_commands::last_insertion(Real w) +{ + PCursor<Command*> pc(*this); + while (pc.ok() && pc->when <= w) + pc++; + return pc; +} + +void +Score_commands::add_seq(svec<Command> com) +{ + if (!com.sz()) + return; + Real when = com[0].when; + + PCursor<Command*> pc(last_insertion(when)); + for (int i = 0; i < com.sz(); i++) { + Command *c = new Command(com[i]); + assert(com[i].when == when); + if (!pc.ok()) + pc.add(c); + else + pc.insert(c); + } +} + +void +Score_commands::set_breakable(Real when) +{ + bool found_typeset(false); + PCursor<Command*> cc = first(when); + for (; cc.ok() && cc->when == when; cc++) { + if (cc->isbreak()) + return; + if (cc->code == TYPESET) + found_typeset=true; + } + + assert(!found_typeset); + + svec<Command> seq; + Command k(when); + k.code = BREAK_PRE; + seq.add(k); + k.code = BREAK_MIDDLE; + seq.add(k); + k.code = BREAK_POST; + seq.add(k); + k.code = BREAK_END; + seq.add(k); + + add_seq(seq); +} + +bool +Score_commands::is_breakable(Real w) +{ + PCursor<Command*> cc = first(w); + for (; cc.ok() && cc->when == w; cc++) { + if (cc->isbreak()) + return true; + } + return false; +} + +void +Score_commands::insert_between(Command victim, PCursor<Command*> firstc, + PCursor<Command*> last) +{ + assert(last->when==firstc->when); + PCursor<Command*> c(firstc+1); + while (c != last) { // hmm what if !last.ok()? + if (victim.priority > c->priority) { + c.insert(new Command(victim)); + return; + } + c++; + } + last.insert(new Command(victim)); +} + +void +Score_commands::add_command_to_break(Command pre, Command mid, Command post) +{ + Real w = pre.when; + PCursor<Command*> c ( first(w)), f(c), l(c); + + while (!c->isbreak()) + c++; + f = c++; + while (!c->isbreak()) + c++; + l = c++; + + insert_between(pre, f, l); + f = l; + while (!c->isbreak()) + c++; + l = c++; + insert_between(mid, f, l); + f = l; + while (!c->isbreak()) + c++; + l = c++; + insert_between(post, f, l); + assert(l.ok() && l->when ==w && l->code == BREAK_END); +} + +void +Score_commands::parser_add(Command *c) +{ + bottom().add(c); +} + +void +Score_commands::process_add(Command c) +{ + bool encapsulate =false; + Real w = c.when; + Command pre(w); + Command mid(w); + Command post(w); + + + if (c.code == TYPESET) { + if (c.args[0] == "BAR") { + set_breakable(w); + encapsulate = true; + mid = c; + pre = c; + } + if (c.args[0] == "METER" && is_breakable(w)) { + encapsulate = true; + mid = c; + pre = c; + post =c; + } + } + + if (encapsulate) + add_command_to_break(pre, mid, post); + else { + svec<Command> seq; + seq.add(c); + add_seq(seq); + } +} + +/* + first and last column should be breakable. + Remove any command past the last musical column. + */ +void +Score_commands::clean(Real l) +{ + assert(l>0); + if (!is_breakable(0.0)) { + Command c(0.0); + c.code = TYPESET; + c.args.add("BAR"); + c.args.add("empty"); + process_add(c); + } + + PCursor<Command*> bot(bottom()); + + while (bot.ok() && bot->when > l) { + mtor <<"removing "; + bot->print(); + bot.del(); + bot = bottom(); + } + + if (!is_breakable(l)) { + Command c(l); + c.code = TYPESET; + c.args.add("BAR"); + c.args.add("||"); + process_add(c); + } + OK(); +} + +void +Score_commands::OK() const +{ + for (PCursor<Command*> cc(*this); cc.ok() && (cc+1).ok(); cc++) { + assert(cc->when <= (cc+1)->when); + if (cc->when == (cc+1)->when && !cc->isbreak() && !(cc+1)->isbreak()) + assert(cc->priority >= (cc+1)->priority); + } +} + +void +Score_commands::print() const +{ + for (PCursor<Command*> cc(*this); cc.ok() ; cc++) { + cc->print(); + } +} + +/* + TODO + */ +Score_commands* +Score_commands::parse(Real l) const +{ + Score_commands*nc = new Score_commands; + int beats_per_meas=4; + Real measlen = 1.0; // 4/4 by default + + Real inbar=0.0; + int barcount=0; + Real wholes=0.0; + Real stoppos=0.0; + + { + Command c(0.0); + c.code = TYPESET; + c.args.add("BAR"); + c.args.add("empty"); + nc->process_add(c); + } + for (PCursor<Command*> cc(*this); cc.ok() && cc->when <= l; cc++) { + assert (cc->code==INTERPRET); + if (cc->args[0] == "METER") { + beats_per_meas = cc->args[1].value(); + int one_beat = cc->args[2].value(); + measlen = beats_per_meas/Real(one_beat); + nc->process_add(*get_meter_command(wholes, beats_per_meas, one_beat)); + } + if (cc->args[0] == "SKIP") { + stoppos = wholes + cc->args[1].value() * measlen + + cc->args[2].fvalue(); + wholes += (measlen-inbar); // skip at least 1 measure + barcount++; + while (wholes <= stoppos) { + nc->process_add(*get_bar_command(wholes)); // liek + wholes += measlen; + barcount ++; + } + wholes = stoppos; + //something + } + } + + return nc; +} diff --git a/src/score.cc b/src/score.cc new file mode 100644 index 0000000000..1df13365f2 --- /dev/null +++ b/src/score.cc @@ -0,0 +1,224 @@ +#include "scommands.hh" +#include "tstream.hh" +#include "score.hh" +#include "sccol.hh" +#include "pscore.hh" +#include "staff.hh" +#include "debug.hh" +#include "paper.hh" + +void +Score::set(Paperdef*p) +{ + delete paper_; + paper_ = p; +} + +void +Score::output(String s) +{ + OK(); + if (paper_->outfile=="") + paper_->outfile = s; + + *mlog << "output to " << paper_->outfile << "...\n"; + Tex_stream the_output(paper_->outfile); + pscore_->output(the_output); +} + + +void +Score::process() +{ + *mlog << "Processing ... "; + set(commands_->parse(last())); + commands_->print(); + + if (!paper_) + paper_ = new Paperdef; + + commands_->clean(last()); + + /// distribute commands to disciples + distribute_commands(); + + pscore_ = new PScore(paper_); + for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) { + sc->set_output(pscore_); + sc->process(); + } + + // do this after processing, staffs first have to generate PCols. + do_pcols(); + calc_idealspacing(); + clean_cols(); + OK(); + // print(); + + pscore_->preprocess(); + *mlog << "Calculating ... "; + pscore_->calc_breaking(); + pscore_->postprocess(); + + // TODO: calculate vertical structs + // TODO: calculate mixed structs. + *mlog << "\n"; +} + +// remove empty cols with no spacing attached. +/* should rethink ownership of cols + */ +void +Score::clean_cols() +{ + for (PCursor<Staff * > sc(staffs_); sc.ok(); sc++) + sc->clean_cols(); + + for (PCursor<Score_column*> c(cols_); c.ok(); ) { + if (!c->pcol->used) { + mtor << "removing : "; + c->print(); + c.del(); + } else + c++; + } + + pscore_->clean_cols(); +} +/* this sux. We should have Score_column create the appropriate PCol. + Unfortunately, PCols don't know about their position. + */ +// todo +PCursor<Score_column*> +Score::create_cols(Real w) +{ + Score_column* c1 = new Score_column(w); + Score_column* c2 = new Score_column(w); + + c1->musical = false; + c2->musical = true; + + PCursor<Score_column*> scc(cols_); + + for (; scc.ok(); scc++) { + assert(scc->when != w); + if (scc->when > w) + break; + } + + if (!scc.ok()) { + cols_.bottom().add(c1); + cols_.bottom().add(c2); + scc = cols_.bottom(); + scc --; + } else { + scc.insert(c1); + scc.insert(c2); + scc -= 2; + } + return scc; +} + +Score_column* +Score::find_col(Real w,bool mus) +{ + PCursor<Score_column*> scc(cols_); + for (; scc.ok(); scc++) { + if (scc->when == w && scc->musical == mus) + return scc; + if (scc->when > w) + break; + } + scc = create_cols(w); + if (mus) + scc++; + return scc; +} + +void +Score::distribute_commands(void) +{ + for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) { + sc->add_commands(*commands_); + } +} +void +Score::add(Staff*s) +{ + s->score_ = this; + staffs_.bottom().add(s); +} + + +void +Score::do_pcols() +{ + PCursor<Score_column*> sc(cols_); + for (; sc.ok(); sc++) { + pscore_->add(sc->pcol); + } +} +Real +Score::last() const +{ + Real l = 0; + for (PCursor<Staff*> stc(staffs_); stc.ok(); stc++) { + l = MAX(l, stc->last()); + } + return l; +} + +void +Score::OK() const +{ +#ifndef NDEBUG + for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) { + sc->OK(); + assert(sc->score_ == this); + } + staffs_.OK(); + cols_.OK(); + for (PCursor<Score_column*> cc(cols_); cc.ok() && (cc+1).ok(); cc++) { + assert(cc->when <= (cc+1)->when); + } + commands_->OK(); +#endif +} + + +void +Score::print() const +{ +#ifndef NPRINT + mtor << "score {\n"; + for (PCursor<Staff*> sc(staffs_); sc.ok(); sc++) { + sc->print(); + } + for (PCursor<Score_column*> sc(cols_); sc.ok(); sc++) { + sc->print(); + } + commands_->print(); + mtor << "}\n"; +#endif +} + +Score::Score() +{ + pscore_=0; + paper_ = 0; + commands_ = new Score_commands; +} + +Score::~Score() +{ + delete pscore_; + delete commands_; + delete paper_; +} + +void +Score::set(Score_commands*c) +{ + delete commands_; + commands_ = c; +} diff --git a/src/scoreline.cc b/src/scoreline.cc new file mode 100644 index 0000000000..7e40c6a76a --- /dev/null +++ b/src/scoreline.cc @@ -0,0 +1,43 @@ +#include "scoreline.hh" +#include "staffline.hh" +#include "dimen.hh" +#include "spanner.hh" +#include "symbol.hh" +#include "paper.hh" +#include "pcol.hh" +#include "pscore.hh" + + +String +Line_of_score::TeXstring() const +{ + String s("\\vbox{%<- line of score\n"); + for (PCursor<Line_of_staff*> sc(staffs); sc.ok(); sc++){ + s += sc->TeXstring(); + if ((sc+1).ok()) + s+= "\\interstaffline\n"; + } + s += "}"; + return s; +} + + +Line_of_score::Line_of_score(svec<const PCol *> sv, + const PScore *ps) +{ + score = ps; + for (int i=0; i< sv.sz(); i++) { + PCol *p=(PCol *) sv[i]; + cols.bottom().add(p); + p->line=this; + } + + for (PCursor<PStaff*> sc(score->staffs); sc.ok(); sc++) + staffs.bottom().add(new Line_of_staff(this, sc)); +} +/* construct a line with the named columns. Make the line field + in each column point to this + + #sv# isn't really const!! + */ + diff --git a/src/scores.cc b/src/scores.cc new file mode 100644 index 0000000000..04efd6e4b1 --- /dev/null +++ b/src/scores.cc @@ -0,0 +1,34 @@ +#include "main.hh" +#include "score.hh" +#include "string.hh" + +static svec<Score*> sv; + +static String outfn="lelie.uit"; + +// todo: check we don't overwrite default output. + +void +do_scores() +{ + for (int i=0; i < sv.sz(); i++) { + sv[i]->process(); + sv[i]->output(outfn); + delete sv[i]; + sv[i] =0; + } +} + +void +add_score(Score * s) +{ + sv.add(s); +} + + +void +set_default_output(String s) +{ + outfn = s; +} + diff --git a/src/simpleprint.cc b/src/simpleprint.cc new file mode 100644 index 0000000000..395396e22b --- /dev/null +++ b/src/simpleprint.cc @@ -0,0 +1,55 @@ +#include "lookup.hh" +#include "request.hh" +#include "beam.hh" +#include "pscore.hh" +#include "paper.hh" +#include "simplestaff.hh" +#include "molecule.hh" +#include "sccol.hh" +#include "stem.hh" +#include "notehead.hh" +#include "rest.hh" +#include "debug.hh" +#include "bar.hh" +#include "meter.hh" + +Item * +Simple_staff::get_TYPESET_item(Command *com) +{ + Item *s; + if (com -> args[0] == "BAR" ) { + s = new Bar(com->args[1]); + } else if (com->args[0] == "METER") { + svec<String> arg( com->args); + arg.del(0); + s = new Meter(arg); + } else + assert(false); + + return s; +} + +void +Simple_column::typeset_item(Item *i, int breakst) +{ + assert(i); + // ugh + staff_->pscore_->typeset_item(i, score_column->pcol, + staff_->theline,breakst); +} + +void +Simple_staff::set_output(PScore* ps ) +{ + pscore_ = ps; + pscore_->add(theline); +} + + +Rest* +Simple_staff::get_rest(Rest_req*rq) +{ + int b = rq->rhythmic()->balltype; + int d = rq->rhythmic()->dots; + return new Rest(b, d); +} diff --git a/src/simplestaff.cc b/src/simplestaff.cc new file mode 100644 index 0000000000..5150d5478a --- /dev/null +++ b/src/simplestaff.cc @@ -0,0 +1,66 @@ +#include "request.hh" +#include "swalker.hh" +#include "debug.hh" +#include "staff.hh" +#include "command.hh" +#include "simplestaff.hh" +#include "sccol.hh" + + + + +Simple_column::Simple_column(Score_column*s, Simple_staff *rs) + : Staff_column(s) +{ + stem_ = 0; + staff_ = rs; + beam_ = 0; +} + +Simple_staff::Simple_staff() +{ + theline = 0; +} + +/** + accept: + + BREAK: all + TYPESET: bar, meter, + + */ + + + +void +Simple_column::process_requests() +{ + for (int i = 0 ; i < v_elts.sz(); i ++) + for (PCursor<Request *> rqc(v_elts[i]->reqs); rqc.ok(); rqc++) { + Request *rq= rqc; + if (rq->rhythmic()){ + notes.add( rq->rhythmic()); + } + if (rq->stem()) { + stem_ = rq->stem(); + } + + if (rq->beam()) { + beam_ = rq->beam(); + } + } +} +Staff_column* +Simple_staff::create_col(Score_column*s) +{ + return new Simple_column(s,this); +} +void +Simple_staff::walk() +{ + for (Simple_walker sc(this); sc.ok(); sc++) { + sc.col()->process_requests();// TODO + sc.process(); + } +} + diff --git a/src/simplewalker.cc b/src/simplewalker.cc new file mode 100644 index 0000000000..ad9019ac33 --- /dev/null +++ b/src/simplewalker.cc @@ -0,0 +1,106 @@ +#include "request.hh" +#include "beam.hh" +#include "pscore.hh" +#include "simplestaff.hh" +#include "sccol.hh" +#include "stem.hh" +#include "notehead.hh" +#include "rest.hh" +#include "debug.hh" + +void +Simple_walker::process_command(Command*com) +{ + switch (com->code){ + case BREAK_PRE: + case BREAK_MIDDLE: + case BREAK_POST: + case BREAK_END: + (*this)->score_column->set_breakable(); + break_status = com->code- BREAK_PRE; + break; + case INTERPRET: + break; + + case TYPESET: + { + Item* i = staff()->get_TYPESET_item(com); + col()->typeset_item(i, break_status); + } + break; + + default : + break; + } +} + +void +Simple_walker::process_requests() +{ + Simple_column*c = col(); + Simple_staff *s = staff(); + if (c->beam_&& c->beam_->spantype == Span_req::START) { + if (beam_) + error("Too many beams"); + beam_ = new Beam; + } + + if (c->stem_) { + stem_ = s->get_stem(c->stem_->stem()); + c->typeset_item(stem_); + } + + for (int i = 0; i < c->notes.sz(); i ++) { + Rhythmic_req*rq = c->notes[i]; + if (rq->note()) { + Notehead*n = s->get_notehead(rq->note()); + stem_->add(n); + noteheads.add(n); + } + + if (rq->rest()) { + c->typeset_item( s->get_rest(rq->rest()) ); + } + } + + + if (beam_) { + beam_->add(stem_); + } + + if (c->beam_&& c->beam_->spantype == Span_req::STOP) { + pscore_->typeset_spanner(beam_, s->theline); + beam_ = 0; + } + for (int i = 0; i < noteheads.sz(); i++) { + c->typeset_item(noteheads[i]); + } + noteheads.set_size(0); + + if (stem_) { + stem_ = 0; + } +} + +Simple_walker::Simple_walker(Simple_staff*s) + : Staff_walker(s, s->theline->pscore_) +{ + stem_ = 0; + beam_ =0; +} + + + +Simple_staff* +Simple_walker::staff() +{ + return (Simple_staff*) staff_; +} + +Simple_column* +Simple_walker::col() +{ + return (Simple_column*) *(*this); +} + + diff --git a/src/spanner.cc b/src/spanner.cc new file mode 100644 index 0000000000..16c3ff19cb --- /dev/null +++ b/src/spanner.cc @@ -0,0 +1,54 @@ +#include "pstaff.hh" +#include "debug.hh" +#include "pscore.hh" +#include "spanner.hh" +#include "symbol.hh" +#include "molecule.hh" +#include "pcol.hh" + +String +Spanner::TeXstring() const +{ + + assert(output); + return output->TeXstring(); +} + +Spanner::Spanner() +{ + pstaff_=0; + left = right = 0; +} + +void +Spanner::process() +{ +} + +void +Spanner::preprocess() +{ +} + +Interval +Spanner::width()const +{ + return Interval(0,right->hpos - left->hpos); +} + +Paperdef* +Spanner::paper()const +{ + assert(pstaff_); + return pstaff_->pscore_->paper_; +} +void +Spanner::print()const +{ +#ifndef NPRINT + mtor << "Spanner { Output "; + output->print(); + + mtor << "}\n"; +#endif +} diff --git a/src/staff.cc b/src/staff.cc new file mode 100644 index 0000000000..3a32801b2f --- /dev/null +++ b/src/staff.cc @@ -0,0 +1,172 @@ +#include "staff.hh" +#include "swalker.hh" +#include "stcol.hh" +#include "sccol.hh" +#include "debug.hh" + +Staff::Staff(Staff const&src) +{ + PL_copy(voices,src.voices); + PL_copy(commands,src.commands); + assert(!cols.size()); // cols is a runtime field. + + score_ = src.score_; + pscore_ = src.pscore_; +} + +Paperdef* +Staff::paper() const{ + return score_->paper_; +} + +void +Staff::clean_cols() +{ + PCursor<Staff_column *> stc(cols); + for(; stc.ok(); ){ + if (!stc->score_column->used()) + stc.del(); + else + stc++; + } +} + +Staff_column * +Staff::get_col(Real w, bool mus) +{ + Score_column* sc = score_->find_col(w,mus); + assert(sc->when == w); + PCursor<Staff_column *> stc(cols); + for (; stc.ok(); stc++) { + if (*stc->score_column > *sc) // too far + break; + if (sc == stc->score_column) + return stc; + } + Staff_column* newst = create_col(sc); + + if (!stc.ok()) { + cols.bottom().add(newst); + return cols.bottom(); + } + + if (mus) { + stc.insert(newst); + return newst; + } + + if ((stc-1)->when() == newst->when()) { + stc--; + } + + stc.insert(newst); + + return newst; +} + + +void +Staff::add_voice(Voice *v) +{ + voices.bottom().add(v); +} + +/* + put all stuff grouped vertically in the Staff_cols + */ +void +Staff::setup_staffcols() +{ + + for (PCursor<Voice*> vc(voices); vc.ok(); vc++) { + + Real now = vc->start; + for (PCursor<Voice_element *> ve(vc->elts); ve.ok(); ve++) { + + Staff_column *sc=get_col(now,true); + sc->add(ve); + now += ve->duration; + } + } + + for (PCursor<Command*> cc(commands); cc.ok(); cc++) { + Staff_column *sc=get_col(cc->when,false); + sc->s_commands.add(cc); + } +} + +/// merge commands from score +void +Staff::add_commands(PointerList<Command*> const &cl) +{ + PCursor<Command*> score_c(cl); + PCursor<Command*> cc(commands); + + while (score_c.ok()) { + while (cc.ok() && cc->when <= score_c->when) + cc++; + + Command*nc = new Command (*(* score_c)); + if (cc.ok()) { + // cc->when > score_c->when + cc.insert( nc ); + } else { + commands.bottom().add( nc); + cc = commands.bottom(); + } + score_c++; + } + + // now integrate break commands with other commands. + // maybe do this in derived functions. +} + +void +Staff::process() +{ + setup_staffcols(); + OK(); + walk(); +} + +void +Staff::OK() const +{ +#ifndef NDEBUG + cols.OK(); + commands.OK(); + voices.OK(); + assert(score_); +#endif +} + + +Real +Staff::last() const +{ + Real l = 0.0; + for (PCursor<Voice*> vc(voices); vc.ok(); vc++) { + l = MAX(l, vc->last()); + } + return l; +} + + +void +Staff::print() const +{ +#ifndef NPRINT + mtor << "Staff {\n"; + for (PCursor<Voice*> vc(voices); vc.ok(); vc++) { + vc->print(); + + } + mtor <<"}\n"; +#endif +} + +Staff::Staff() +{ + score_ =0; + pscore_=0; +} diff --git a/src/staffline.cc b/src/staffline.cc new file mode 100644 index 0000000000..c83e904bc6 --- /dev/null +++ b/src/staffline.cc @@ -0,0 +1,107 @@ +#include "staffline.hh" +#include "scoreline.hh" +#include "dimen.hh" +#include "spanner.hh" +#include "symbol.hh" +#include "paper.hh" +#include "pcol.hh" +#include "pscore.hh" + +static String +make_vbox(Interval i) +{ + String s("\\vbox to "); + s += print_dimen(i.length()); + s += "{\\vskip "+print_dimen(i.max)+" "; + return s; +} + + +String +Line_of_staff::TeXstring() const +{ + String s("%line_of_staff\n"); + s+=make_vbox(height()); + // the staff itself: eg lines, accolades + s += "\\hbox{"; + { + Symbol sym = pstaff_->get_stafsym(scor->score->paper_->linewidth); + s+=sym.tex; + PCursor<const PCol *> cc(scor->cols); + Real lastpos=cc->hpos; + + // all items in the current line & staff. + for (; cc.ok(); cc++) { + Real delta=cc->hpos - lastpos; + lastpos = cc->hpos; + + // moveover + if (delta) + s +=String( "\\kern ") + print_dimen(delta); + + // now output the items. + + for (PCursor<const Item *> ic(cc->its); ic.ok(); ic++) { + if (ic->pstaff_ == pstaff_) + s += ic->TeXstring(); + } + // spanners. + for (PCursor<const Spanner *> sc(cc->starters); sc.ok(); sc++) + if (sc->pstaff_ == pstaff_) + s += sc->TeXstring(); + } + } + s+="\\hss}\\vss}"; + return s; +} + +Line_of_staff::Line_of_staff(Line_of_score * sc, PStaff*st) +{ + scor=sc; + pstaff_=st; +#if 0 + const PCol *linestart = sc->cols.top(); + const PCol *linestop = sc->cols.bottom(); + + for (PCursor<const Spanner*> sp(pstaff_->spans); sp.ok(); sp++) { + const PCol *brokenstart = &MAX(*linestart, *sp->left); + const PCol *brokenstop = &MIN(*linestop, *sp->right); +// if (*brokenstop < *brokenstart) + brokenspans.bottom().add(sp->broken_at(0,0)); + } +#endif + for (PCursor<const Spanner*> sp(pstaff_->spans); sp.ok(); sp++) { + + brokenspans.bottom().add(sp->broken_at(0,0)); + } +} + + +Interval +Line_of_staff::height() const +{ + Interval y; + { + Symbol s = pstaff_->stafsym->eval(scor->score->paper_->linewidth); + y = s.dim.y; + } + PCursor<const PCol *> cc(scor->cols); + + // all items in the current line & staff. + for (; cc.ok(); cc++) { + for (PCursor<const Item *> ic(cc->its); ic.ok(); ic++) { + if (ic->pstaff_ == pstaff_) { + y.unite(ic->height()); + } + + // spanners. + for (PCursor<const Spanner *> sc(cc->starters); sc.ok(); sc++) + if (sc->pstaff_ == pstaff_) { + y.unite(sc->height()); + } + } + } + return y; +} + + diff --git a/src/stcol.cc b/src/stcol.cc new file mode 100644 index 0000000000..799daed316 --- /dev/null +++ b/src/stcol.cc @@ -0,0 +1,30 @@ +#include "stcol.hh" +#include "sccol.hh" +#include "voice.hh" + +bool +Staff_column::mus() const +{ + return score_column->musical; +} + +Real +Staff_column::when() const +{ + return score_column->when; +} + +void +Staff_column::add(Voice_element*ve) +{ + Real d= ve->duration; + if (d){ + score_column->durations.add(d); + } + + v_elts.add(ve); +} + +Staff_column::Staff_column(Score_column*s) { + score_column = s; +} diff --git a/src/stem.cc b/src/stem.cc new file mode 100644 index 0000000000..fdb47f9755 --- /dev/null +++ b/src/stem.cc @@ -0,0 +1,156 @@ +#include "stem.hh" +#include "dimen.hh" +#include "debug.hh" +#include "paper.hh" +#include "notehead.hh" +#include "lookup.hh" +#include "molecule.hh" +#include "pcol.hh" + +const int STEMLEN=7; +static int +ABS(int i) { + return (i < 0)?-i:i; +} + + +Stem::Stem(int c) +{ + minnote = 1000; // invalid values + maxnote = -1000; + bot = top = 0; + flag = 4; + dir =0; + staff_center=c; + stemlen=0; + print_flag=true; + stem_xoffset=0; +} + + +void +Stem::set_stemend(Real se) +{ + // todo: margins + assert((dir > 0 && se >= maxnote) || (se <= minnote && dir <0)); + + top = (dir < 0) ? maxnote : se; + bot = (dir < 0) ? se : minnote; + flag = dir*ABS(flag); +} + +void +Stem::add(Notehead *n) +{ + if (n->balltype == 1) + return; + int p = n->position; + if ( p < minnote) + minnote = p; + if ( p> maxnote) + maxnote = p; +} +void +Stem::print()const +{ + mtor << "Stem minmax=["<< minnote<<","<<maxnote<<"], flag: "<<flag; + Item::print(); +} + +void +Stem::set_default_dir() +{ + Real mean = (minnote+maxnote)/2; + dir = (mean > staff_center) ? -1: 1; +} + +void +Stem::set_default_stemlen() +{ + if (!dir) + set_default_dir(); + + int stafftop = 2*staff_center; + stemlen = STEMLEN + (maxnote - minnote); + + // uhh... how about non 5-line staffs? + if (maxnote < -2 && dir == 1){ + int t = staff_center - staff_center/2; + stemlen = t - minnote +2; + } else if (minnote > stafftop + 2 && dir == -1) { + int t = staff_center + staff_center/2; + stemlen = maxnote -t +2; + } + + assert(stemlen); +} + + +void +Stem::set_default_extents() +{ + assert(minnote<=maxnote); + + if (!stemlen) + set_default_stemlen(); + + set_stemend((dir< 0) ? maxnote-stemlen: minnote +stemlen); + if (dir > 0){ + stem_xoffset = paper()->note_width(); + } +} + +void +Stem::postprocess() +{ + set_default_extents(); + brew_molecole(); +} + +Interval +Stem::width()const +{ + if (!print_flag || ABS(flag) <= 4) + return Interval(0,0); // TODO! + Paperdef*p= paper(); + return p->lookup_->flag(flag).dim.x; +} + +void +Stem::brew_molecole() +{ + assert(pstaff_); + assert(bot!=top); + assert(!output); + + Paperdef *p =paper(); + Parametric_symbol *stem = p->lookup_->stem(); + + Real dy = p->interline()/2; + String y1 =print_dimen( dy * bot); + String y2 = print_dimen(dy * top); + Symbol ss =stem->eval(y1,y2); + delete stem; + + output = new Molecule(Atom(ss)); + + if (print_flag&&ABS(flag) > 4){ + Symbol fl = p->lookup_->flag(flag); + Molecule m(fl); + if (flag < -4){ + output->add_bot(m); + } else if (flag > 4) { + output->add_top(m); + } else + assert(false); + } + + output->translate(Offset(stem_xoffset,0)); + +} + +Real +Stem::hpos()const +{ + return pcol_->hpos + stem_xoffset; +} diff --git a/src/swalker.cc b/src/swalker.cc new file mode 100644 index 0000000000..db48fd1f82 --- /dev/null +++ b/src/swalker.cc @@ -0,0 +1,32 @@ +#include "request.hh" +#include "swalker.hh" +#include "stcol.hh" + +Staff_walker::~Staff_walker() {} + +Staff_walker::Staff_walker(Staff * s, PScore*ps ) + : PCursor<Staff_column*> (s->cols) +{ + staff_ = s; + pscore_ = ps; + break_status = BREAK_END - BREAK_PRE; +} + +Real +Staff_walker::when() const +{ + return (* (PCursor<Staff_column*> *) this)->when(); +} + +void +Staff_walker::process() +{ + break_status = BREAK_END - BREAK_PRE; + + for (int i = 0 ; i < (*this)->s_commands.sz(); i++) { + process_command((*this)->s_commands[i]); + } + + process_requests(); +} + diff --git a/src/symbol.cc b/src/symbol.cc new file mode 100644 index 0000000000..907f1d7282 --- /dev/null +++ b/src/symbol.cc @@ -0,0 +1,32 @@ +#include "symbol.hh" + +Symbol::Symbol() + : dim(Interval(0,0),Interval(0,0)) +{ + tex = "\\unknown"; +} +Symbol::Symbol(String s, Box b) + : dim(b) +{ + tex = s; +} + +/****************************************************************/ + +Symbol +Parametric_symbol::eval(String args1)const +{ + svec<String> a; + a.add(args1); + return eval(a); +} + +Symbol +Parametric_symbol::eval(String args1,String arg2)const +{ + svec<String> a; + a.add(args1); + a.add(arg2); + return eval(a); +} + diff --git a/src/symtable.cc b/src/symtable.cc new file mode 100644 index 0000000000..a28d14670c --- /dev/null +++ b/src/symtable.cc @@ -0,0 +1,60 @@ +#include "misc.hh" +#include "dimen.hh" +#include "debug.hh" +#include "real.hh" +#include "symbol.hh" +#include "assoc.hh" +#include "symtable.hh" + + +Symbol +Symtable::lookup(String s) const +{ + if (elt_query(s)) + return (*this)[s]; + else { + error( "Unknown symbol " +s+'\n'); + } +} + +Symtable* +Symtables::operator()(String s) +{ + return Assoc<String, Symtable*>::operator[](s); +} + +void +Symtables::read(Text_db &symini) +{ + while (!symini.eof()) { + Text_record r( symini++); + if (r[0] == "end" ) + return; + assert (r[0] == "table"); + + String tabnam = r[1]; + Symtable * sp = new Symtable; + while (!symini.eof()){ + r = symini++; + if (r[0] == "end") + break; + + if (r.sz() != 6) + error("Not enough fields in symbol init"); + + int i=0; + String id=r[i++]; + String tex=r[i++]; + svec<Real> dims; + for (int j=0; j < 4; j++) + dims.add( parse_dimen(r[i++])); + + Symbol s(tex, Box(dims)); + (*sp)[id] = s; + } + (*this)[tabnam] = sp; + } +} + + + diff --git a/src/table.cc b/src/table.cc new file mode 100644 index 0000000000..1f302259e9 --- /dev/null +++ b/src/table.cc @@ -0,0 +1,67 @@ +#include "glob.hh" +#include "debug.hh" +#include "string.hh" +#include "identifier.hh" +#include "keyword.hh" +#include "associter.hh" +#include "parser.hh" + +static Keyword_ent the_key_tab[]={ + "voice", VOICE, + "rhythmstaff", RHYTHMSTAFF, + "melodicstaff", MELODICSTAFF, + "score", SCORE, + "bar", BAR, + "output", OUTPUT, + "cm", CM, + "start", START_T, + "pt", PT, + "in", IN, + "mm", MM, + "paper", PAPER, + "width", WIDTH, + "meter", METER, + "unitspace", UNITSPACE, + "skip", SKIP, + "octave", OCTAVECOMMAND, + "commands", COMMANDS, + "staff", STAFF, + "geometric", GEOMETRIC, + "duration", DURATIONCOMMAND, + 0,0 +} ; + + +int +lookup_keyword(String s) +{ + static Keyword_table table(the_key_tab); + return table.lookup(s); +} + +Assoc<String, Identifier*> the_id_tab; + +Identifier* +lookup_identifier(String s) +{ + if (!the_id_tab.elt_query(s)) + return 0; + + return the_id_tab[s]; +} + +void +add_identifier(Identifier*i) +{ + the_id_tab[i->name] = i; +} + +void +delete_identifiers() +{ + + for (Assoc_iter<String,Identifier*> ai(the_id_tab); ai.ok(); ai++) { + mtor << "deleting: " << ai.key()<<'\n'; + delete ai.val(); + } +} diff --git a/src/template1.cc b/src/template1.cc new file mode 100644 index 0000000000..fd2431130e --- /dev/null +++ b/src/template1.cc @@ -0,0 +1,25 @@ + +#include "plist.hh" +#include "pcol.hh" +#include "item.hh" +#include "request.hh" +#include "command.hh" +#include "spanner.hh" +#include "scoreline.hh" +#include "staffline.hh" + +#include "list.cc" +#include "plist.cc" +#include "cursor.cc" + +#define PLC_instantiate(a) PL_instantiate(a); PL_instantiate(const a) + +L_instantiate(Stem*); +PLC_instantiate(Line_of_score); +PLC_instantiate(Line_of_staff); +PLC_instantiate(Item); +PLC_instantiate(Spanner); +PLC_instantiate(PStaff); +PLC_instantiate(Idealspacing); +PLC_instantiate(PCol); + diff --git a/src/template2.cc b/src/template2.cc new file mode 100644 index 0000000000..01695c863f --- /dev/null +++ b/src/template2.cc @@ -0,0 +1,19 @@ + +#include "symbol.hh" +#include "voice.hh" + +#include "staff.hh" +#include "sccol.hh" +#include "stcol.hh" +#include "spanner.hh" + +#include "list.cc" +#include "plist.cc" +#include "cursor.cc" +PL_instantiate(Score_column); +PL_instantiate(Staff_column); +PL_instantiate(Staff); +PL_instantiate(Voice_element); +PL_instantiate(Voice); + + diff --git a/src/template3.cc b/src/template3.cc new file mode 100644 index 0000000000..92921bb29a --- /dev/null +++ b/src/template3.cc @@ -0,0 +1,12 @@ +#include "request.hh" +#include "command.hh" +#include "molecule.hh" +#include "list.cc" +#include "plist.cc" +#include "cursor.cc" +PL_instantiate(Atom); +PL_instantiate(Command); +PL_instantiate(Request); + + + diff --git a/src/tex.cc b/src/tex.cc new file mode 100644 index 0000000000..ae9dc1b5f3 --- /dev/null +++ b/src/tex.cc @@ -0,0 +1,32 @@ +#include "dimen.hh" +#include "tex.hh" +#include "symbol.hh" +#include "const.hh" + +String +vstrut(Real h) +{ + return String("\\vrule height ") + print_dimen(h) + "depth 0pt width 0pt"; +} + + +static void +substitute_arg(String& r, String arg) +{ + int p = r.pos('%'); + if (!p ) return ; + else p--; + r = r.left(p) + arg + r.right(r.len() - p -1); +} + + +String +substitute_args(String source, svec<String> args) +{ + String retval (source); + for (int i = 0 ; i < args.sz(); i++) + substitute_arg(retval, args[i]); + while (retval.pos('%')) + substitute_arg(retval, ""); + return retval; +} diff --git a/src/texbeam.cc b/src/texbeam.cc new file mode 100644 index 0000000000..e21efba0be --- /dev/null +++ b/src/texbeam.cc @@ -0,0 +1,99 @@ +/* + + Code to generate beams for TeX + + */ + + #include <math.h> +#include "symbol.hh" +#include "molecule.hh" +#include "tex.hh" +#include "symtable.hh" +#include "dimen.hh" +#include "lookup.hh" + +Symbol +Lookup::beam_element(int sidx, int widx, Real slope) +{ + Symbol bs=(*symtables_)("beamslopes")->lookup("slope"); + + svec<String> args; + args.add(sidx); + args.add(widx); + bs.tex = substitute_args(bs.tex,args); + int w = 2 << widx; + Real width = convert_dimen(w,"pt"); + bs.dim.x = Interval(0,width); + bs.dim.y = Interval(0,width*slope); + return bs; +} + +// ugh.. hard wired tex-code. +static int +slope_index(Real &s) +{ + assert(ABS(s) < 0.45); + int i = int(rint(s * 20.0)); + + s = i/20.0; + if (s>0) + return 6*i +122; + else + return -6 * i+ 188; +} + +Symbol +Lookup::rule_symbol(Real height, Real width) +{ + Symbol bs=(*symtables_)("beamslopes")->lookup("horizontal"); + svec<String> args; + args.add(print_dimen(height)); + args.add(print_dimen(width)); + bs.tex = substitute_args(bs.tex,args); + bs.dim.x = Interval(0,width); + bs.dim.y = Interval(0,height); + return bs; +} + +Symbol +Lookup::beam(Real &slope, Real width) +{ + int sidx = slope_index(slope); + if (!slope) + return rule_symbol(convert_dimen(2,"pt"), width); + + Real w = width; + Real elemwidth = convert_dimen(64,"pt"); + int widx = 5; + + Molecule m; + Real dy=0; + Real minwid =convert_dimen(2,"pt"); + assert(w > minwid); + while (w > minwid) { + while (elemwidth > w) { + widx --; + elemwidth /= 2.0; + } + + Atom a(beam_element(sidx, widx, slope)); + a.translate(Offset(0, dy)); + m.add_right(a); + dy += elemwidth*slope; + w -= elemwidth; + } + + widx = 0; + Atom a(beam_element(sidx, widx, slope)); + a.translate(Offset(width -minwid, (width-minwid) * slope)); + m.add(a); + + Symbol ret; + ret.tex = m.TeXstring(); + ret.dim.y = Interval(0,width*slope); + ret.dim.x = Interval(0,width); + + return ret; +} + + diff --git a/src/tstream.cc b/src/tstream.cc new file mode 100644 index 0000000000..05e2e9c93e --- /dev/null +++ b/src/tstream.cc @@ -0,0 +1,75 @@ +#include <fstream.h> +#include <time.h> +#include "tex.hh" +#include "main.hh" +#include "tstream.hh" +#include "debug.hh" + +Tex_stream::Tex_stream(String filename) +{ + os = new ofstream(filename); + if (!*os) + error("can't open `" + filename+"\'"); + nest_level = 0; + outputting_comment=false; + header(); +} +void +Tex_stream::header() +{ + *os << "% Creator: " << get_version(); + *os << "% Automatically generated, at "; + time_t t(time(0)); + *os << ctime(&t); +//*os << "% from input file ..\n"; +} +Tex_stream::~Tex_stream() +{ + delete os; + assert(nest_level == 0); +} + +// print string. don't forget indent. +Tex_stream & +Tex_stream::operator<<(String s) +{ + + for (const char *cp = s; *cp; cp++) { + if (outputting_comment) { + *os << *cp; + if (*cp == '\n') { + outputting_comment=false; + + } + continue; + } + switch(*cp) + { + case '%': + outputting_comment = true; + *os << *cp; + break; + case '{': + nest_level++; + *os << *cp; + break; + case '}': + nest_level--; + *os << *cp; + assert (nest_level >= 0); + /* FALTHROUGH */ + + case '\n': + *os << "%\n"; + *os << String(' ', nest_level); + break; + default: + *os << *cp; + break; + } + } + return *this; +} + + +/****************************************************************/ diff --git a/src/version.cc b/src/version.cc new file mode 100644 index 0000000000..401273b6b1 --- /dev/null +++ b/src/version.cc @@ -0,0 +1,9 @@ +#include "version.hh" + +static char *s = "LilyPond version " VERSIONSTR " compiled on " __DATE__ " at " __TIME__ "\n"; + +const char * +get_version() +{ + return s; +} diff --git a/src/voice.cc b/src/voice.cc new file mode 100644 index 0000000000..14883e1499 --- /dev/null +++ b/src/voice.cc @@ -0,0 +1,78 @@ +#include "debug.hh" +#include "voice.hh" + +Voice::Voice(Voice const&src) +{ + PL_copy(elts, src.elts); + start = src.start; +} + +Voice::Voice() +{ + start = 0.0; +} + +void +Voice::add(Voice_element*v) +{ + elts.bottom().add(v); +} + +void +Voice::print() const +{ + #ifndef NPRINT + + mtor << "start: "<< start<<eol; + for (PCursor<Voice_element*> vec(elts); vec.ok(); vec++) + vec->print(); +#endif +} + +Real +Voice::last() const +{ + Real l =start; + for (PCursor<Voice_element*> vec(elts); vec.ok(); vec++) + l += vec->duration; + return l; +} +/****************************************************************/ +void +Voice_element::print() const +{ +#ifndef NPRINT + mtor << "voice_element { dur :"<< duration <<"\n"; + for (PCursor<Request*> rc(reqs); rc.ok(); rc++) { + rc->print(); + } + mtor << "}\n"; +#endif +} +void +Voice_element::add(Request*r) +{ + if (r->rhythmic()) { + assert (!duration); + duration = r->duration(); + } + r->elt = this; + reqs.bottom().add(r); +} + + +Voice_element::Voice_element() +{ + voice = 0; + group = 0; + duration = 0.0; +} + +Voice_element::Voice_element(Voice_element const&src) +{ + duration=src.duration; + voice=src.voice; + PointerList__copy(Request*, reqs, src.reqs, clone()); + group=src.group; + assert(!granted_items.size() && !granted_spanners.size()); +} diff --git a/src/warn.cc b/src/warn.cc new file mode 100644 index 0000000000..426173ee0c --- /dev/null +++ b/src/warn.cc @@ -0,0 +1,18 @@ +#include "debug.hh" + ostream &warnout (cerr); + ostream *mlog(&cerr); + + + +void warning(String s) +{ + WARN << s; +} + + +void error(String s) +{ + cerr << "\n" << s << "\nexiting..\n"; + exit(1); +} + |