]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/chord.cc
release: 1.1.31
[lilypond.git] / lily / chord.cc
index 26ae4ce2714059b200324da0238a0327321aa83c..e08ce282e65f36f5a6a0996b824446ab52beadef 100644 (file)
 #include "chord.hh"
 #include "warn.hh"
 
+// doesn't seem common, and we should know about this during purple hit
+// #define INVERSION_ADDED_AS_BASE 1
+
 Chord::Chord (Array<Musical_pitch> pitch_arr)
 {
   pitch_arr_ = pitch_arr;
 }
 
-// construct from parser output
-// urg: should split this up into understandable chunks
-Chord::Chord (Musical_pitch tonic, Array<Musical_pitch>* add_arr_p, Array<Musical_pitch>* sub_arr_p, Musical_pitch* inversion_p)
+static void
+rebuild_transpose (Musical_pitch tonic, Array<Musical_pitch>* pitch_arr_p)
 {
-  for (int i = 0; i < add_arr_p->size (); i++)
+  for (int i = 0; i < pitch_arr_p->size (); i++)
     {
       Musical_pitch p = tonic;
-      Musical_pitch q = (*add_arr_p)[i];
+      Musical_pitch q = (*pitch_arr_p)[i];
       // duh, c7 should mean <c bes>
       if (q.notename_i_ == 6)
         q.accidental_i_--;
       p.transpose (q);
-      (*add_arr_p)[i] = p;
+      (*pitch_arr_p)[i] = p;
     }
-  add_arr_p->sort (Musical_pitch::compare);
-  for (int i = 0; i < sub_arr_p->size (); i++)
+  pitch_arr_p->sort (Musical_pitch::compare);
+}
+
+static int
+find_pitch_i (Array<Musical_pitch> const* pitch_arr_p, Musical_pitch p)
+{
+  for (int i = 0; i < pitch_arr_p->size (); i++)
+    if (p == (*pitch_arr_p)[i])
+      return i;
+  return -1;
+}
+
+static int
+find_notename_i (Array<Musical_pitch> const* pitch_arr_p, Musical_pitch p)
+{
+  int i = find_pitch_i (pitch_arr_p, p);
+  if (i == -1)
     {
-      Musical_pitch p = tonic;
-      Musical_pitch q = (*sub_arr_p)[i];
-      // duh, c7 should mean <c bes>
-      if (q.notename_i_ == 6)
-        q.accidental_i_--;
-      p.transpose (q);
-      (*sub_arr_p)[i] = p;
+      for (int i = 0; i < pitch_arr_p->size (); i++)
+       {
+         p.octave_i_ = (*pitch_arr_p)[i].octave_i_;
+         if (p == (*pitch_arr_p)[i])
+           return i;
+       }
+    }
+  return i;
+}
+
+static int
+trap_i (Musical_pitch tonic, Musical_pitch p)
+{
+  int i = p.notename_i_ - tonic.notename_i_
+    + (p.octave_i_ - tonic.octave_i_) * 7;
+  while (i < 0)
+    i += 7;
+  i++;
+  return i;
+}
+
+static Array<Musical_pitch>
+missing_triads_pitch_arr (Array<Musical_pitch>const* pitch_arr_p)
+{
+  Array<Musical_pitch> triads;
+
+  /* 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++)
+    triads.push (Musical_pitch( 2, minormajor_a[i]));
+
+  Musical_pitch tonic = (*pitch_arr_p)[0];
+  Musical_pitch last = tonic;
+  Array<Musical_pitch> missing_arr;
+
+  for (int i = 0; i < pitch_arr_p->size ();)
+    {
+      Musical_pitch p = (*pitch_arr_p)[i];
+      int trap = trap_i (tonic, p);
+      if (last.notename_i_ == p.notename_i_)
+       last.transpose (triads[(last.notename_i_ - tonic.notename_i_ + 7) % 7]);
+      if (trap > trap_i (tonic, last))
+       {
+         while (trap > trap_i (tonic, last))
+           {
+             if ((last.notename_i_ - tonic.notename_i_ + 7) % 7 == 6)
+               {
+                 Musical_pitch special_seven = last;
+                 Musical_pitch lower (0, -1);
+                 special_seven.transpose (lower);
+                 missing_arr.push (special_seven);
+               }
+             else
+               {
+                 missing_arr.push (last);
+               }
+             last.transpose (triads[(last.notename_i_ - tonic.notename_i_ + 7) % 7]);
+           }
+       }
+      else
+       {
+         i++;
+       }
     }
-  sub_arr_p->sort (Musical_pitch::compare);
+  return missing_arr;
+}
 
-  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);
+/*
+  construct from parser output
+*/
+Chord::Chord (Musical_pitch tonic, Array<Musical_pitch>* add_arr_p, Array<Musical_pitch>* sub_arr_p, Musical_pitch* inversion_p)
+{
+  rebuild_transpose (tonic, add_arr_p);
+  rebuild_transpose (tonic, sub_arr_p);
+
+  Musical_pitch fifth = tonic;
+  fifth.transpose (Musical_pitch (2));
+  fifth.transpose (Musical_pitch (2, -1));
 
   /*
-   must have minimum at 5 (3 is added automatically as missing)
+    default chord includes upto 5: <1, 3, 5>
    */
-  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<Musical_pitch> 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 
+  add_arr_p->insert (tonic, 0);
+  int highest_trap = trap_i (tonic, add_arr_p->top ());
+  if (highest_trap < 5)
+    add_arr_p->push (fifth);
 
   /*
-    if first addition is 4, assume sus4 and don't add third implicitely
+    find missing triads
    */
-  Musical_pitch sus (3);
-  sus.transpose (tonic);
-  if (add_arr_p->size ())
-    if ((*add_arr_p)[0] == sus)
-      missing.transpose (mthird);
+  Array<Musical_pitch> missing_arr = missing_triads_pitch_arr (add_arr_p);
 
   /*
-   add missing triads
+    if additions include 4, assume sus4 and don't add third implicitely
    */
-  for (int i = 0; i < add_arr_p->size ();)
+  Musical_pitch third = tonic;
+  third.transpose (Musical_pitch (2));
+  Musical_pitch sus = tonic;
+  sus.transpose (Musical_pitch (3));
+  if (::find_pitch_i (add_arr_p, sus) != -1)
     {
-      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++;
+      int i = ::find_pitch_i (&missing_arr, third);
+      if (i != -1)
+       missing_arr.get (i);
     }
-
+  
   /*
-    add tonic
+    complete the list of triads to be added
    */
-  if (!add_arr_p->size () || ((*add_arr_p)[0] != tonic))
-    add_arr_p->insert (tonic, 0);
+  add_arr_p->concat (missing_arr);
+  add_arr_p->sort (Musical_pitch::compare);
 
   /*
    add all that aren't subtracted
@@ -132,6 +179,8 @@ Chord::Chord (Musical_pitch tonic, Array<Musical_pitch>* add_arr_p, Array<Musica
         pitch_arr_.push (p);
     }
 
+  pitch_arr_.sort (Musical_pitch::compare);
+
   for (int i = 0; i < sub_arr_p->size (); i++)
     warning (_f ("invalid subtraction: not part of chord: %s",
                 (*sub_arr_p)[i].str ()));
@@ -140,122 +189,169 @@ Chord::Chord (Musical_pitch tonic, Array<Musical_pitch>* add_arr_p, Array<Musica
     {
       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 ((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 ()));
+       {
+         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);
+       {
+#if INVERSION_ADDED_AS_BASE
+         pitch_arr_.insert (pitch_arr_[i], 0);
+         rebuild_with_bass (0);
 #else
-         /*
-           or
-           2. insert inversion as lowest (at first position)
-         */
          rebuild_with_bass (i);
 #endif
+         
        }
       delete inversion_p;
     }
 }
 
+void
+Chord::find_additions_and_subtractions(Array<Musical_pitch>* add_arr_p, Array<Musical_pitch>* sub_arr_p)
+{
+  Musical_pitch tonic = pitch_arr_[0];
+  /*
+    all the triads that should be there
+   */
+  Array<Musical_pitch> all_arr;
+  all_arr.push (tonic);
+  all_arr.push (pitch_arr_.top ());
+  all_arr.concat (missing_triads_pitch_arr (&all_arr));
+  all_arr.sort (Musical_pitch::compare);
+  
+  int i = 0;
+  int j = 0;
+  while ((i < all_arr.size ()) || (j < pitch_arr_.size ()))
+    {
+      i = i <? all_arr.size () - 1;
+      j = j <? pitch_arr_.size () - 1;
+      Musical_pitch a = all_arr[i];
+      Musical_pitch p = pitch_arr_[j];
+      if (a == p)
+       {
+         i++;
+         j++;
+       }
+      else if ((p < a) || (p.notename_i_ == a.notename_i_))
+       {
+         add_arr_p->push (p);
+         j++;
+       }
+      else
+       {
+         sub_arr_p->push (a);
+         i++;
+       }
+    }
+      
+  /*
+    add highest addition, because it names chord
+   */
+  if (trap_i (tonic, pitch_arr_.top () > 5))
+    add_arr_p->push (pitch_arr_.top ());
+}
+
 String
 Chord::banter_str (Musical_pitch* inversion) const
 {
   Musical_pitch tonic = pitch_arr_[0];
 
-  Array<Musical_pitch> 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;
+  Array<Musical_pitch> add_arr;
+  Array<Musical_pitch> sub_arr;
+  find_additions_and_subtractions (&add_arr, &sub_arr);
+                          
+
+  Array<Musical_pitch> scale;
+  for (int i=0; i < 7; i++)
+    scale.push (Musical_pitch (i));
+
+  // 7 always means 7-...
+  //  scale.push (Musical_pitch (6, -1)); // b
+
+  rebuild_transpose (tonic, &scale);
+  
+  bool has3m_b = false;
+  bool has4_b = false;
+  String str;
   String sep_str;
-  String sub_sep_str;
-  int last_trap = 1;
-  for (int i=1; i < pitch_arr_.size (); i++)
+  for (int i = 0; i < add_arr.size (); i++)
     {
-      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)
-       {
-         last_trap += 2;
-         sub_str += sub_sep_str + "no" + to_str (last_trap);
-         sub_sep_str = "/";
-       }
-      last_trap = trap;
+      Musical_pitch p = add_arr[i];
+      int trap = trap_i (tonic, p);
+      if (trap == 4)
+       has4_b = true;
       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))))
+       {
+         tonic_str += "m";
+         has3m_b = true;
+       }
+      else if (accidental
+              || (!(trap % 2) || ((i + 1 == add_arr.size ()) && (trap > 5))))
         {
-         add_str += sep_str;
+         str += sep_str;
           if ((trap == 7) && (accidental == 1))
-            add_str += "maj7";
+            str += "maj7";
           else
             {
-              add_str += to_str (trap);
+              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 = "";
-               }
+                str += accidental < 0 ? "-" : "+";
             }
          sep_str = "/";
        }
     }
 
+  for (int i = 0; i < sub_arr.size (); i++)
+    {
+      Musical_pitch p = sub_arr[i];
+      int trap = trap_i (tonic, p);
+      /*
+       if chord has 3-, assume minor and don't display 'no3'
+       if additions include 4, assume sus4 and don't display 'no3'
+      */
+      if (!((trap == 3) && (has3m_b || has4_b)))
+       {
+         str += sep_str + "no" + to_str (trap);
+         sep_str = "/";
+       }
+    }
+
   String inversion_str;
   if (inversion)
     {
       inversion_str = inversion->str ();
       inversion_str = "/" + inversion_str.left_str (1).upper_str ()
-       + acc[tonic.accidental_i_ + 2];
+       + acc[inversion->accidental_i_ + 2];
 
     }
 
-  if (sub_str.length_i ())
-    sub_str = sep_str + sub_str;
-  String str = tonic_str + "$^{" + add_str + sub_str + "}$" + inversion_str;
-  return str;
+  return tonic_str + "$^{" + str + "}$" + inversion_str;
+}
+
+int
+Chord::find_notename_i (Musical_pitch p) const
+{
+  return ::find_notename_i (&pitch_arr_, p);
+}
+
+int
+Chord::find_pitch_i (Musical_pitch p) const
+{
+  return ::find_pitch_i (&pitch_arr_, p);
 }
 
 int
@@ -318,6 +414,7 @@ Chord::find_tonic_i () const
 void
 Chord::rebuild_from_base (int base_i)
 {
+  assert (base_i >= 0);
   Musical_pitch last (0, 0, -5);
   Array<Musical_pitch> new_arr;
   for (int i = 0; i < pitch_arr_.size (); i++)
@@ -338,6 +435,12 @@ Chord::rebuild_from_base (int base_i)
 void
 Chord::rebuild_insert_inversion (int tonic_i)
 {
+  assert (tonic_i > 0);
+#if INVERSION_ADDED_AS_BASE
+  // inversion was added; don't insert
+  Musical_pitch inversion = pitch_arr_.get (0);
+  (void)inversion;
+#else
   Musical_pitch inversion = pitch_arr_.get (0);
   rebuild_from_base (tonic_i - 1);
   if (pitch_arr_.size ())
@@ -352,11 +455,13 @@ Chord::rebuild_insert_inversion (int tonic_i)
        pitch_arr_.insert (inversion, i);
        break;
       }
+#endif
 }
 
 void
 Chord::rebuild_with_bass (int bass_i)
 {
+  assert (bass_i >= 0);
   Musical_pitch inversion = pitch_arr_.get (bass_i);
   // is lowering fine, or should others be raised?
   if (pitch_arr_.size ())