#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
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 ()));
{
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
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++)
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 ())
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 ())