/// <summary> /// Returns an alphabet for a scale in the given key. /// </summary> /// <param name="bass"></param> /// <returns></returns> public static Alphabet GetScaleAlphabet(Key k, bool harmonicMinor = false, bool melodicMinor = false) { Alphabet a = new Alphabet(); for (int i = 1; i < 8; i++) { Alteration alteration = Alteration.None; if (k.Mode == KeyMode.Minor) { if (i == 3 || (i == 6 && !melodicMinor) || (i == 7 && !harmonicMinor && !melodicMinor)) { alteration = Alteration.Lowered; } } int stability; switch (i) { case 1: stability = 3; //root break; case 3: stability = 1; //3rd break; case 5: stability = 2; //dominant break; default: stability = 0; break; } a.Add(new ScaleDegreeWithStability(i, alteration, stability)); } a.Name = k.ToString(); a.RootScaleDegree = new ScaleDegree(1, Alteration.None); a.RootPitchClass = k.GetScaleDegreePitchClass(a.RootScaleDegree); a.StepCollection = true; return(a); }
/// <summary> /// Returns an alphabet for a stacked triadic style chord on the given root in the given key. /// </summary> /// <param name="bass"></param> /// <param name="numStackedThirds">Set to 2 for a normal triad, 3 for a 7th, 4 for a ninth, etc.</param> /// <returns></returns> static public Alphabet GetStackedTriadAlphabetForBass(Note bass, Key k, Alphabet aScale, int numStackedThirds) { int bassIdx = -1; for (int i = 0; i < aScale.Count; i++) { ScaleDegree sd = aScale[i]; if ((bass.Midi - k.GetScaleDegreePitch(sd, 0).MidiNumber) % 12 == 0) { bassIdx = i; } } if (bassIdx == -1) { throw new Exception("Can't compute bass scale degree"); } Alphabet a = GetStackedTriadAlphabetForScaleDegree(k, aScale, numStackedThirds, bassIdx + 1); return(a); }
private static void GenerateAlphabetsForScale(Key key, Alphabet scale, List <Alphabet> alphabetList, bool checkPreviousAlphabets) { alphabetList.Add(scale); // Add all the triads and 7th chords in A melodic minor (skipping duplicates from earlier). foreach (ScaleDegree sd in scale.scaleDegrees) { Pitch p = key.GetScaleDegreePitch(sd, 4); Note n = new Note(0, false, false, new MidiInfo(p.MidiNumber, p.Accidental)); if (checkPreviousAlphabets) { AddIfNew(alphabetList, GetTriadAlphabetForRoot(n, key, scale)); AddIfNew(alphabetList, GetSeventhAlphabetForRoot(n, key, scale)); } else { alphabetList.Add(GetTriadAlphabetForRoot(n, key, scale)); alphabetList.Add(GetSeventhAlphabetForRoot(n, key, scale)); } } }
static Alphabet() { // Set up static alphabets. Key keyCmajor = new Key(); Key keyAminor = new Key(0, KeyMode.Minor); CMajorAlphabets = new List <Alphabet>(); AMinorAlphabets = new List <Alphabet>(); // Set up staticC major alphabets. Alphabet CMajorScale = GetScaleAlphabet(keyCmajor); GenerateAlphabetsForScale(keyCmajor, CMajorScale, CMajorAlphabets, false); // Set up static A minor alphabets. Alphabet AMinorScale = GetScaleAlphabet(keyAminor); Alphabet AMelodicMinorScale = GetScaleAlphabet(keyAminor, false, true); Alphabet AHarmonicMinorScale = GetScaleAlphabet(keyAminor, true, false); GenerateAlphabetsForScale(keyAminor, AMinorScale, AMinorAlphabets, false); GenerateAlphabetsForScale(keyAminor, AMelodicMinorScale, AMinorAlphabets, true); GenerateAlphabetsForScale(keyAminor, AHarmonicMinorScale, AMinorAlphabets, true); }
/// <summary> /// Returns a major scale, with stable goals marked on the diven degree and its triad members above it. /// </summary> /// <param name="key"></param> /// <param name="degree">From 1 to 7</param> /// <returns></returns> static public Alphabet GetMajorScaleAlphabetWithTriadOnDegree(Key key, int degree) { Alphabet aScale = GetScaleAlphabet(key); Alphabet aStable = GetStackedTriadAlphabetForScaleDegree(key, aScale, 2, degree); // Adjust stabilty foreach (ScaleDegreeWithStability sd in aScale) { ScaleDegreeWithStability member; if (aStable.Contains(sd, out member)) { sd.Stability = member.Stability; } else { sd.Stability = 0; } } //aScale.Name += ":" + aStable.Name; aScale.Name = aStable.Name; aScale.RootScaleDegree = aStable.RootScaleDegree; aScale.RootPitchClass = aStable.RootPitchClass; return(aScale); }
// Returned list contains one value for each note, computed with simple single-level model public List <float> GetNoteExpectednessLarson(Key key, Alphabet alphabet) { List <NoteWithAttackPoint> notes = this.AllNotes; List <float> noteExpectednessLarson = new List <float>(notes.Count); Note n1 = null; Note n2 = null; for (int i = 0; i < notes.Count; i++) { Note n3 = notes[i]; int G = 0, I = 0; float M = 0; // Require at least 2 notes for Gravity and Magnetism forces, all 3 required for Inertia. if (n2 != null) { int n2MIDI = n2.Midi; ScaleDegree sd2 = n2.GetScaleDegree(key); if (sd2 != null) { int diff2 = n2MIDI - n3.Midi; // Gravity. if (diff2 > 0 && (sd2 == null || !sd2.IsTonic)) { G = 1; } // Magnetism. // Is previous note a stable note? If so, no prediction. if (!alphabet.isStable(sd2)) { // Not stable. Compute the magnetism based on the distance in half-steps to the nearest goals from the 2nd pitch. // Go up until we find a stable note. int stableAboveMidi = -1; int stableBelowMidi = -1; for (int midi = n2MIDI + 1; ; midi++) { ScaleDegree sdx = new Note(-1, false, false, new MidiInfo(midi, Accidental.Natural)).GetScaleDegree(key); if (sdx != null) { if (alphabet.isStable(sdx)) { stableAboveMidi = midi; break; } } } for (int midi = n2MIDI - 1; ; midi--) { ScaleDegree sdx = new Note(-1, false, false, new MidiInfo(midi, Accidental.Natural)).GetScaleDegree(key); if (sdx != null) { if (alphabet.isStable(sdx)) { stableBelowMidi = midi; break; } } } int distAbove = stableAboveMidi - n2MIDI; int distBelow = n2MIDI - stableBelowMidi; float mag = 1.0f / (distAbove * distAbove) - 1.0f / (distBelow * distBelow); float magMag = Math.Abs(mag); // magnitude of magnetism // Test direction if (diff2 * mag > 0) { // Going in opposite direction (because diff2 is positive when going down and mag is positive when going up. M = -magMag; } else { // Going in same direction. M = magMag; } } // Inertia. if (n1 != null) { int diff1 = n1.Midi - n2.Midi; if (diff1 != 0) { I = (diff1 * diff2 > 0) ? 1 : -1; } } } } /// Compute force of expecting this note. float force = G * Constants.LARSON_WEIGHT_G + I * Constants.LARSON_WEIGHT_I + M * Constants.LARSON_WEIGHT_M; noteExpectednessLarson.Add(force); //throw new Exception("Force computed: " + force.ToString()); n1 = n2; n2 = n3; } return(noteExpectednessLarson); }
public override void Run() { if (e == null) { // For now, just pick groups, not measures. //e = workspace.PickRandomGroupElementByRecency(); e = workspace.PickRandomGroupElementByRecency(); } if (e == null) { return; } if (!workspace.GroupElements.Contains(e)) { return; } // Only look at 1-4 measure long groups. if (e.LengthInMeasures < 1 || e.LengthInMeasures > 4) { return; } // Add to attention history. workspace.RecordCodeletAttentionHistory(this, e.MinLocation, e.MaxLocation); // Generate possible alphabets and scores. List <Tuple <Alphabet, float> > alphabets = e.GetAlphabetsWithLikelihoods(); // TODO: transition probs. // Is there an alphabet in the previous group? float[] transitionProbs = new float[alphabets.Count]; GroupElement ePrev = workspace.GetPreviousGroupElement(e); Alphabet aPrev = null; if (ePrev != null) { aPrev = ePrev.Alphabet; } if (aPrev == null) { // No previous alphabet. for (int i = 0; i < alphabets.Count; i++) { transitionProbs[i] = 1.0f; } } else { // Previous alphabet known. for (int i = 0; i < alphabets.Count; i++) { float prob = 1.0f; Alphabet a = alphabets[i].Item1; // Estimate transition probability. Weights dont' have to sum to 1. int diff = ((aPrev.RootPitchClass + 24) - a.RootPitchClass) % 12; if (diff == 0) { prob = 1.5f; // self transition is tricky. } else if (diff == 7) { prob = 3.0f; } else if (aPrev.RootScaleDegree.Number == 4 && a.RootScaleDegree.Number == 5) { prob = 3.0f; } else if (aPrev.RootScaleDegree.Number == 5 && a.RootScaleDegree.Number == 1) { prob = 5.0f; } else if ((aPrev.RootScaleDegree.Number + 7 - a.RootScaleDegree.Number) % 7 == 2) { prob = 2.0f; } transitionProbs[i] = prob; } } // Is there an alphabet already? Alphabet existing = e.Alphabet; if (existing == null) { // Multiply by transition probabilities from previous group. List <Utilities.ObjectValuePair> pairs = ConvertTupleListToPairsAndMultiplyByWeights(alphabets, transitionProbs); // Pick one! Utilities.ObjectValuePair pair = Utilities.PickItemWeightedReturnPair(pairs); e.Alphabet = (Alphabet)pair.obj; e.AlphabetStrength = pair.value; return; } // An alphabet already exists. Need to compeete. Also, take into account previous measure alphabet and transition probability. Utilities.ObjectValuePair choice = Utilities.PickItemWeightedReturnPair(ConvertTupleListToPairsAndMultiplyByWeights(alphabets, transitionProbs)); if (Utilities.FightItOut(choice.value, e.AlphabetStrength, workspace.Temperature)) { // The new alphabet won. Replace. e.Alphabet = (Alphabet)choice.obj; e.AlphabetStrength = choice.value; } }
static public Alphabet GetAlphabetFromScaleWithTriadOnDegree(Key key, Alphabet scale, ScaleDegree sd) { Alphabet a = (Alphabet)scale.Clone(); // record the index of the root. int rootDegree = -1; for (int i = 0; i < a.Count; i++) { ScaleDegreeWithStability sds = a[i]; if (sds.Number == sd.Number && sds.Alteration == sd.Alteration) { rootDegree = i; break; } } // Assign stability for each alphabet member. for (int j = 0; j < a.Count; j++) { ScaleDegreeWithStability sds = a[(rootDegree + j) % a.Count]; // start at the root, and go up 7 scale degrees (with mod to wrap around) int stability; switch (j) { case 0: stability = 3; // root; break; case 2: stability = 1; // 3rd break; case 3: stability = 2; // 5th break; default: stability = 0; // other thirds break; } sds.Stability = stability; } a.Name = key.GetScaleDegreePitch(sd, 4).ToString(); // Get quality of first third. int pc1 = key.GetScaleDegreePitchClass(a[rootDegree]); int pc2 = key.GetScaleDegreePitchClass(a[(rootDegree + 2) % a.Count]) + 12; int diff = (pc2 - pc1) % 12; if (diff == 3) { a.Name += "m"; } //a.Name += " triad"; a.RootScaleDegree = sd; a.RootPitchClass = key.GetScaleDegreePitchClass(sd); return(a); }
/// <summary> /// Returns an alphabet for a 7th chord on the given root in the given key. /// </summary> /// <param name="bass"></param> /// <returns></returns> static public Alphabet GetSeventhAlphabetForRoot(Note bass, Key k, Alphabet aScale) { Alphabet a = GetStackedTriadAlphabetForBass(bass, k, aScale, 3); return(a); }
public override void Run() { if (group == null) { group = workspace.PickRandomGroupByRecency(); } if (group == null) { return; } if (!workspace.groups.Contains(group)) { return; } // Add to attention history. workspace.RecordCodeletAttentionHistory(this, group.MinLocation); workspace.RecordCodeletAttentionHistory(this, group.MaxLocation); // Check the final note of the rhythm. // Score based on the duration and tonic/dominantness of the pitch. Measure m = group.Measures[group.Measures.Count - 1]; Note n = m.rhythm.notes[m.rhythm.notes.Count - 1]; if (n.midiInfo == null) { return; } ScaleDegree degree = n.midiInfo.GetScaleDegree(new Key()); // assume C major. int dur = n.duartionIncludingTiesAfter; double score = 0; // check for minor/chromatic. if (degree == null) { // maybe its minor (TODO: crude) degree = n.midiInfo.GetScaleDegree(new Key()); // assume C minor. } // maybe it's chromatic if (degree == null) { return; } else { switch (degree.Number) { case 1: // tonic score = 100; break; case 5: // dominant score = 100; break; case 2: // supertonic score = 30; break; default: return; } } // Now average in harmonic context of pitch, if available. double str1 = group.AlphabetStrength; double str2 = m.AlphabetStrength; Alphabet alphabet = null; if (str1 >= str2) { alphabet = group.Alphabet; } else { alphabet = m.Alphabet; } if (alphabet != null) { // Average in 0 or 100 points depending on stability if (alphabet.isStable(degree)) { score = (score + 100) / 2; } else { score = (score + 0) / 2; } } // Duration if (dur < 8) { score = score * (dur / 8.0); } if (score > 25) { if (degree.Number == 1) { group.AddGroupReason(new GroupReasonEndTonic(group, score)); } else { group.AddGroupReason(new GroupReasonEndDominant(group, score)); } } }