X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fchord.cc;h=bb41246ffac176ea9a1491429c560dab4d2d21cc;hb=6b89a99dbfb2e208c42ba1510be9e638d21be4a9;hp=26ae4ce2714059b200324da0238a0327321aa83c;hpb=5f94c3ae8a1804e880693edbc28d2c6a838d3149;p=lilypond.git diff --git a/lily/chord.cc b/lily/chord.cc index 26ae4ce271..bb41246ffa 100644 --- a/lily/chord.cc +++ b/lily/chord.cc @@ -3,364 +3,370 @@ source file of the GNU LilyPond music typesetter - (c) 1999 Jan Nieuwenhuizen + (c) 1999--2002 Jan Nieuwenhuizen */ #include "chord.hh" +#include "musical-request.hh" #include "warn.hh" +#include "debug.hh" +#include "music-list.hh" +#include "musical-request.hh" -Chord::Chord (Array pitch_arr) + +SCM +Chord::base_pitches (SCM tonic) { - pitch_arr_ = pitch_arr; + SCM base = SCM_EOL; + + SCM major = Pitch (0, 2, 0).smobbed_copy (); + SCM minor = Pitch (0, 2, -1).smobbed_copy (); + + base = gh_cons (tonic, base); + base = gh_cons (ly_pitch_transpose (ly_car (base), major), base); + base = gh_cons (ly_pitch_transpose (ly_car (base), minor), base); + + return scm_reverse_x (base, SCM_EOL); } -// construct from parser output -// urg: should split this up into understandable chunks -Chord::Chord (Musical_pitch tonic, Array* add_arr_p, Array* sub_arr_p, Musical_pitch* inversion_p) +SCM +Chord::transpose_pitches (SCM tonic, SCM pitches) { - for (int i = 0; i < add_arr_p->size (); i++) - { - Musical_pitch p = tonic; - Musical_pitch q = (*add_arr_p)[i]; - // duh, c7 should mean - if (q.notename_i_ == 6) - q.accidental_i_--; - p.transpose (q); - (*add_arr_p)[i] = p; - } - add_arr_p->sort (Musical_pitch::compare); - for (int i = 0; i < sub_arr_p->size (); i++) + /* map? + hoe doe je lambda in C? + */ + SCM transposed = SCM_EOL; + for (SCM i = pitches; gh_pair_p (i); i = ly_cdr (i)) { - Musical_pitch p = tonic; - Musical_pitch q = (*sub_arr_p)[i]; - // duh, c7 should mean - if (q.notename_i_ == 6) - q.accidental_i_--; - p.transpose (q); - (*sub_arr_p)[i] = p; - } - sub_arr_p->sort (Musical_pitch::compare); - - Musical_pitch third (2); - Musical_pitch mthird (2, -1); - Musical_pitch missing; - missing = tonic; - missing.transpose (third); - - Musical_pitch p; - p = tonic; - p.transpose (third); - p.transpose (mthird); - - /* - must have minimum at 5 (3 is added automatically as missing) - */ - if (!add_arr_p->size ()) - add_arr_p->push (p); - else if ((add_arr_p->top () < p) && (add_arr_p->top ().notename_i_ != p.notename_i_)) - add_arr_p->push (p); - add_arr_p->sort (Musical_pitch::compare); - - Array triads; - triads.push (third); // c e - triads.push (mthird); // d f - triads.push (mthird); // e g - triads.push (third); // f a - triads.push (third); // g b - triads.push (mthird); // a c - triads.push (mthird); // b d - - /* - if first addition is 4, assume sus4 and don't add third implicitely - */ - Musical_pitch sus (3); - sus.transpose (tonic); - if (add_arr_p->size ()) - if ((*add_arr_p)[0] == sus) - missing.transpose (mthird); - - /* - add missing triads - */ - for (int i = 0; i < add_arr_p->size ();) - { - Musical_pitch p = (*add_arr_p)[i]; - if (p > missing) - while (p > missing) - { - if (p.notename_i_ != missing.notename_i_) - { - if ((missing.notename_i_ - tonic.notename_i_ + 7) % 7 == 6) - { - Musical_pitch special_seven = missing; - Musical_pitch lower (0, -1); - special_seven.transpose (lower); - add_arr_p->insert (special_seven, i++); - } - else - add_arr_p->insert (missing, i++); - } - missing.transpose (triads[(missing.notename_i_ - tonic.notename_i_ + 7) % 7]); - } - else if (p.notename_i_ == missing.notename_i_) - missing.transpose (triads[(missing.notename_i_ - tonic.notename_i_ + 7) % 7]); - else - i++; + transposed = gh_cons (ly_pitch_transpose (tonic, ly_car (i)), + transposed); } + return scm_reverse_x (transposed, SCM_EOL); +} - /* - add tonic - */ - if (!add_arr_p->size () || ((*add_arr_p)[0] != tonic)) - add_arr_p->insert (tonic, 0); +/* + burp, in SCM duw je gewoon een (if (= (step x) 7) (...)) door pitches - /* - add all that aren't subtracted - */ - for (int i = 0; i < add_arr_p->size (); i++) + Lower step STEP. + If step == 0, lower all. + */ +SCM +Chord::lower_step (SCM tonic, SCM pitches, SCM step) +{ + SCM lowered = SCM_EOL; + for (SCM i = pitches; gh_pair_p (i); i = ly_cdr (i)) { - Musical_pitch p = (*add_arr_p)[i]; - int j = 0; - for (; j < sub_arr_p->size (); j++) - if (p == (*sub_arr_p)[j]) - { - sub_arr_p->del (j); - j = -1; - break; - } - if (j == sub_arr_p->size ()) - pitch_arr_.push (p); + SCM p = ly_car (i); + if (gh_equal_p (step_scm (tonic, ly_car (i)), step) + || gh_scm2int (step) == 0) + { + p = ly_pitch_transpose (p, Pitch (0, 0, -1).smobbed_copy ()); + } + lowered = gh_cons (p, lowered); } + return scm_reverse_x (lowered, SCM_EOL); +} - for (int i = 0; i < sub_arr_p->size (); i++) - warning (_f ("invalid subtraction: not part of chord: %s", - (*sub_arr_p)[i].str ())); - - if (inversion_p) +/* Return member that has same notename, disregarding octave or alterations */ +SCM +Chord::member_notename (SCM p, SCM pitches) +{ + /* If there's an exact match, make sure to return that */ + SCM member = gh_member (p, pitches); + if (member == SCM_BOOL_F) { - int i = 0; - for (; i < pitch_arr_.size (); i++) - if ((pitch_arr_[i].notename_i_ == inversion_p->notename_i_) - && (pitch_arr_[i].accidental_i_ == inversion_p->accidental_i_)) - break; - if (i == pitch_arr_.size ()) - warning (_f ("invalid inversion pitch: not part of chord: %s", - inversion_p->str ())); - else - { - /* - urg - should be run-time switchable "chordInversionPreserve", howto? - - there are two ways commonly used to rearrange a chord with - an inversion: - - 1. rebuild pitch list, taking inversion as base - */ -#if 0 - rebuild_from_base (i); -#else + for (SCM i = pitches; gh_pair_p (i); i = ly_cdr (i)) + { /* - or - 2. insert inversion as lowest (at first position) - */ - rebuild_with_bass (i); -#endif + Urg, eindelijk gevonden: () != #f, kan maar niet aan wennen. + Anders kon iets korter... + */ + if (unsmob_pitch (p)->notename_i_ + == unsmob_pitch (ly_car (i))->notename_i_) + { + member = ly_car (i); + break; + } } - delete inversion_p; } + else + member = ly_car (member); + return member; } -String -Chord::banter_str (Musical_pitch* inversion) const +/* Return member that has same notename and alteration, disregarding octave */ +SCM +Chord::member_pitch (SCM p, SCM pitches) { - Musical_pitch tonic = pitch_arr_[0]; - - Array scale; - scale.push (Musical_pitch (0)); // c - scale.push (Musical_pitch (1)); // d - scale.push (Musical_pitch (2)); // e - scale.push (Musical_pitch (3)); // f - scale.push (Musical_pitch (4)); // g - scale.push (Musical_pitch (5)); // a - // 7 always means 7-... - scale.push (Musical_pitch (6, -1)); // b - - - for (int i = 0; i < scale.size (); i++) - scale[i].transpose (tonic); - - //urg, should do translation in scheme. - char const *acc[] = {"\\textflat\\textflat ", "\\textflat ", "", "\\textsharp " , "\\textsharp\\textsharp "}; - String tonic_str = tonic.str (); - tonic_str = tonic_str.left_str (1).upper_str () - + acc[tonic.accidental_i_ + 2]; - - String add_str; - String sub_str; - String sep_str; - String sub_sep_str; - int last_trap = 1; - for (int i=1; i < pitch_arr_.size (); i++) + /* If there's an exact match, make sure to return that */ + SCM member = gh_member (p, pitches); + if (member == SCM_BOOL_F) { - Musical_pitch p = pitch_arr_[i]; - int trap = p.notename_i_ - tonic.notename_i_ - + (p.octave_i_ - tonic.octave_i_) * 7; - while (trap < 0) - trap += 7; - trap++; - while (trap - last_trap > 2) + for (SCM i = pitches; gh_pair_p (i); i = ly_cdr (i)) { - last_trap += 2; - sub_str += sub_sep_str + "no" + to_str (last_trap); - sub_sep_str = "/"; - } - last_trap = trap; - int accidental = p.accidental_i_ - scale[(trap - 1) % 7].accidental_i_; - if ((trap == 3) && (accidental == -1)) - tonic_str += "m"; // hmm - else if (accidental || (!(trap % 2) || ((i + 1 == pitch_arr_.size ()) && (trap > 5)))) - { - add_str += sep_str; - if ((trap == 7) && (accidental == 1)) - add_str += "maj7"; - else - { - add_str += to_str (trap); - if (accidental) - add_str += accidental < 0 ? "-" : "+"; - // catch "C4/no3"; remove "no3" - if (trap == 4) - { - int i = sub_str.index_i ("no3"); - if (i != -1) - sub_str = sub_str.nomid_str (i, 3); - if (!sub_str.length_i ()) - sub_sep_str = ""; - } - } - sep_str = "/"; + if (unsmob_pitch (p)->notename_i_ + == unsmob_pitch (ly_car (i))->notename_i_ + && unsmob_pitch (p)->alteration_i_ + == unsmob_pitch (ly_car (i))->alteration_i_) + { + member = ly_car (i); + break; + } } } + else + member = ly_car (member); + return member; +} - String inversion_str; - if (inversion) - { - inversion_str = inversion->str (); - inversion_str = "/" + inversion_str.left_str (1).upper_str () - + acc[tonic.accidental_i_ + 2]; +SCM +Chord::step_scm (SCM tonic, SCM p) +{ + /* De Pitch intervaas is nog beetje sleutelgat? */ + int i = unsmob_pitch (p)->notename_i_ + - unsmob_pitch (tonic)->notename_i_ + + (unsmob_pitch (p)->octave_i_ + - unsmob_pitch (tonic)->octave_i_) * 7; + while (i < 0) + i += 7; + i++; + return gh_int2scm (i); +} - } +/* + Assuming that PITCHES is a chord, with tonic (CAR PITCHES), find + missing thirds, only considering notenames. Eg, for - if (sub_str.length_i ()) - sub_str = sep_str + sub_str; - String str = tonic_str + "$^{" + add_str + sub_str + "}$" + inversion_str; - return str; -} + PITCHES = c gis d' + + return + + MISSING = e b' -int -Chord::find_tonic_i () const +*/ +SCM +Chord::missing_thirds (SCM pitches) { - /* - find tonic - - first try: base of longest line of triads - */ - int tonic_i = 0; - int longest_i = 0; - for (int i = 0; i < pitch_arr_.size (); i++) + SCM thirds = SCM_EOL; + + /* is the third c-e, d-f, etc. small or large? */ + int minormajor_a[] = {0, -1, -1, 0, 0, -1, -1}; + for (int i=0; i < 7; i++) + thirds = gh_cons (Pitch (0, 2, minormajor_a[i]).smobbed_copy (), + thirds); + thirds = scm_vector (scm_reverse_x (thirds, SCM_EOL)); + + SCM tonic = ly_car (pitches); + SCM last = tonic; + SCM missing = SCM_EOL; + + for (SCM i = pitches; gh_pair_p (i);) { - int no_triad_i = 0; - int last_i = pitch_arr_[i % pitch_arr_.size ()].notename_i_; - int j = 0; - for (; j < pitch_arr_.size (); j++) + SCM p = ly_car (i); + int step = gh_scm2int (step_scm (tonic, p)); + + if (unsmob_pitch (last)->notename_i_ == unsmob_pitch (p)->notename_i_) { - int cur_i = pitch_arr_[(i + j + 1) % pitch_arr_.size ()].notename_i_; - int gap = cur_i - last_i; - while (gap < 0) - gap += 7; - gap %= 7; - if (gap == 2) - last_i = cur_i; - else - no_triad_i++; + int third = (unsmob_pitch (last)->notename_i_ + - unsmob_pitch (tonic)-> notename_i_ + 7) % 7; + last = ly_pitch_transpose (last, scm_vector_ref (thirds, gh_int2scm (third))); } - if (j - no_triad_i > longest_i) + + if (step > gh_scm2int (step_scm (tonic, last))) { - longest_i = j - no_triad_i; - tonic_i = i; + while (step > gh_scm2int (step_scm (tonic, last))) + { + missing = gh_cons (last, missing); + int third = (unsmob_pitch (last)->notename_i_ + - unsmob_pitch (tonic)->notename_i_ + 7) % 7; + last = ly_pitch_transpose (last, scm_vector_ref (thirds, + gh_int2scm (third))); + } + } + else + { + i = ly_cdr (i); } } + + return lower_step (tonic, missing, gh_int2scm (7)); +} + +/* Return PITCHES with PITCH added not as lowest note */ +SCM +Chord::add_above_tonic (SCM pitch, SCM pitches) +{ + /* Should we maybe first make sure that PITCH is below tonic? */ + if (pitches != SCM_EOL) + while (Pitch::less_p (pitch, ly_car (pitches)) == SCM_BOOL_T) + pitch = ly_pitch_transpose (pitch, Pitch (1, 0, 0).smobbed_copy ()); + + pitches = gh_cons (pitch, pitches); + return scm_sort_list (pitches, Pitch::less_p_proc); +} - /* - second try: note after biggest gap - */ - int biggest_i = 0; - // if (longest_i) - if (longest_i <= 1) - for (int i = 0; i < pitch_arr_.size (); i++) - { - int gap = pitch_arr_[i].notename_i_ - - pitch_arr_[(i - 1 + pitch_arr_.size ()) - % pitch_arr_.size ()].notename_i_; - while (gap < 0) - gap += 7; - gap %= 7; - if (gap > biggest_i) - { - biggest_i = gap; - tonic_i = i; - } - } - return tonic_i; +/* Return PITCHES with PITCH added as lowest note */ +SCM +Chord::add_below_tonic (SCM pitch, SCM pitches) +{ + if (pitches != SCM_EOL) + while (Pitch::less_p (ly_car (pitches), pitch) == SCM_BOOL_T) + pitch = ly_pitch_transpose (pitch, Pitch (-1, 0, 0).smobbed_copy ()); + return gh_cons (pitch, pitches); } -void -Chord::rebuild_from_base (int base_i) + + +/* + Parser stuff + + Construct from parser output: + + PITCHES is the plain chord, it does not include bass or inversion + + Part of Chord:: namespace for now, because we do lots of + chord-manipulating stuff. +*/ +SCM +Chord::tonic_add_sub_to_pitches (SCM tonic, SCM add, SCM sub) { - Musical_pitch last (0, 0, -5); - Array new_arr; - for (int i = 0; i < pitch_arr_.size (); i++) + /* urg: catch dim modifier: 3rd, 5th, 7th, .. should be lowered */ + bool dim_b = false; + for (SCM i = add; gh_pair_p (i); i = ly_cdr (i)) { - Musical_pitch p = pitch_arr_[(base_i + i) % pitch_arr_.size ()]; - if (p < last) - { - p.octave_i_ = last.octave_i_; - if (p < last) - p.octave_i_++; + Pitch* p = unsmob_pitch (ly_car (i)); + /* Ugr + This chord modifier stuff should really be fixed + Cmaj7 yields C 7/7- + */ + if (p->octave_i () == -100) + { + p->octave_i_ = 0; + dim_b = true; } - new_arr.push (p); - last = p; } - pitch_arr_ = new_arr; -} + add = transpose_pitches (tonic, add); + add = lower_step (tonic, add, gh_int2scm (7)); + add = scm_sort_list (add, Pitch::less_p_proc); + add = ly_unique (add); + + sub = transpose_pitches (tonic, sub); + sub = lower_step (tonic, sub, gh_int2scm (7)); + sub = scm_sort_list (sub, Pitch::less_p_proc); + + /* default chord includes upto 5: <1, 3, 5> */ + add = gh_cons (tonic, add); + SCM tmp = add; + + SCM fifth = ly_last (base_pitches (tonic)); + int highest_step = gh_scm2int (step_scm (tonic, ly_last (tmp))); + if (highest_step < 5) + tmp = ly_snoc (fifth, tmp); + else if (dim_b) + { + add = lower_step (tonic, add, gh_int2scm (5)); + add = lower_step (tonic, add, gh_int2scm (7)); + } -void -Chord::rebuild_insert_inversion (int tonic_i) -{ - Musical_pitch inversion = pitch_arr_.get (0); - rebuild_from_base (tonic_i - 1); - if (pitch_arr_.size ()) + /* find missing thirds */ + SCM missing = missing_thirds (tmp); + if (highest_step < 5) + missing = ly_snoc (fifth, missing); + + /* if dim modifier is given: lower all missing */ + if (dim_b) + missing = lower_step (tonic, missing, gh_int2scm (0)); + + /* if additions include any 3, don't add third */ + SCM third = ly_cadr (base_pitches (tonic)); + if (member_notename (third, add) != SCM_BOOL_F) + missing = scm_delete (third, missing); + + /* if additions include any 4, assume sus4 and don't add third implicitely + C-sus (4) = c f g (1 4 5) */ + SCM sus = ly_pitch_transpose (tonic, Pitch (0, 3, 0).smobbed_copy ()); + if (member_notename (sus, add) != SCM_BOOL_F) + missing = scm_delete (third, missing); + + /* if additions include some 5, don't add fifth */ + if (member_notename (fifth, add) != SCM_BOOL_F) + missing = scm_delete (fifth, missing); + + /* complete the list of thirds to be added */ + add = gh_append2 (missing, add); + add = scm_sort_list (add, Pitch::less_p_proc); + + SCM pitches = SCM_EOL; + /* Add all that aren't subtracted */ + for (SCM i = add; gh_pair_p (i); i = ly_cdr (i)) { - inversion.octave_i_ = pitch_arr_[0].octave_i_ - 1; - while (inversion < pitch_arr_[0]) - inversion.octave_i_++; + SCM p = ly_car (i); + SCM s = member_notename (p, sub); + if (s != SCM_BOOL_F) + sub = scm_delete (s, sub); + else + pitches = gh_cons (p, pitches); } - for (int i = 0; i < pitch_arr_.size (); i++) - if (pitch_arr_[i] > inversion) - { - pitch_arr_.insert (inversion, i); - break; - } + pitches = scm_sort_list (pitches, Pitch::less_p_proc); + + for (SCM i = sub; gh_pair_p (i); i = ly_cdr (i)) + warning (_f ("invalid subtraction: not part of chord: %s", + unsmob_pitch (ly_car (i))->str ())); + + return pitches; } -void -Chord::rebuild_with_bass (int bass_i) + +/* --Het lijkt me dat dit in het paarse gedeelte moet. */ +Simultaneous_music * +Chord::get_chord (SCM tonic, SCM add, SCM sub, SCM inversion, SCM bass, SCM dur) { - Musical_pitch inversion = pitch_arr_.get (bass_i); - // is lowering fine, or should others be raised? - if (pitch_arr_.size ()) - while (inversion > pitch_arr_[0]) - inversion.octave_i_--; - pitch_arr_.insert (inversion, 0); + SCM pitches = tonic_add_sub_to_pitches (tonic, add, sub); + SCM list = SCM_EOL; + if (inversion != SCM_EOL) + { + /* If inversion requested, check first if the note is part of chord */ + SCM s = member_pitch (inversion, pitches); + if (s != SCM_BOOL_F) + { + /* Then, delete and add as base note, ie: the inversion */ + pitches = scm_delete (s, pitches); + Note_req* n = new Note_req; + n->set_mus_property ("pitch", ly_car (add_below_tonic (s, pitches))); + n->set_mus_property ("duration", dur); + n->set_mus_property ("inversion", SCM_BOOL_T); + list = gh_cons (n->self_scm (), list); + scm_gc_unprotect_object (n->self_scm ()); + } + else + warning (_f ("invalid inversion pitch: not part of chord: %s", + unsmob_pitch (inversion)->str ())); + } + + /* Bass is easy, just add if requested */ + if (bass != SCM_EOL) + { + Note_req* n = new Note_req; + n->set_mus_property ("pitch", ly_car (add_below_tonic (bass, pitches))); + n->set_mus_property ("duration", dur); + n->set_mus_property ("bass", SCM_BOOL_T); + list = gh_cons (n->self_scm (), list); + scm_gc_unprotect_object (n->self_scm ()); + } + + for (SCM i = pitches; gh_pair_p (i); i = ly_cdr (i)) + { + Note_req* n = new Note_req; + n->set_mus_property ("pitch", ly_car (i)); + n->set_mus_property ("duration", dur); + list = gh_cons (n->self_scm (), list); + scm_gc_unprotect_object (n->self_scm ()); + } + + Simultaneous_music*v = new Request_chord (SCM_EOL); + v->set_mus_property ("elements", list); + + return v; } + +