/// <summary> /// Evaluates the ratio of extreme pitch intervals. /// <para> This evaluation checks the pitch interval between every two consecutive /// notes and check if the interval is considered extreme or not, in regard to the /// <paramref name="maxInterval"/> parameter: If the interval is higher than the /// max interval parameter, the distance between the notes would be considered as /// an extreme distance. </para> /// <para> Besides counting the amount of existing extreme intervals, /// this evaluation takes into account the extreme level, i.e., how large in /// terms of semitones the interval is, so a two octave interval is considered twice /// as extreme than a one octave interval. </para> /// <para> The overall fitness is a weighted sum of the ratio between the non-extreme /// intervals to the total number of consecutive notes intervals, and the invert ratio /// of total semitone distance in the entire melody and the max pitch range. </para> /// </summary> /// <param name="maxInterval"> Max interval that is considered "okay" between two /// consecutive notes. Any interval that exceeds this one would be considered extreme. /// </param> /// <param name="candidate"> The melody candidate to evaluate. </param> /// <returns> The fitness outcome score for the requested evaluation. </returns> private protected double EvaluateExtremeIntervals(MelodyCandidate candidate, PitchInterval maxInterval = PitchInterval.PerfectFifth) { // initialize counters & accumuators ulong totalNumOfIntervals = 0; ulong numOfExtremeIntervals = 0; ulong overallDeviation = 0; int adjacentDistance = 0; // init max pitch range int pitchRange = MaxPitch - MinPitch; // initialize metrics double extremeIntervalMetric; double overallDeviationMetric; // convert max interval nullable int to int int maxDistance = (int)maxInterval; // retrieve and filter all pitches which are not hold/rest notes NotePitch[] pitches = candidate.Bars.GetAllPitches().ToArray(); // accumulate extreme adjacent notes pitch distance for (int i = 0; i < pitches.Length - 1; i++) { // calculate pitch distance between the two adjacent notes adjacentDistance = Math.Abs(pitches[i + 1] - pitches[i]); // if this pitch is extreme, update counter & deviation accumulator if (adjacentDistance > maxDistance) { numOfExtremeIntervals++; overallDeviation += (ulong)(adjacentDistance - maxDistance); } } // set total number of intervals totalNumOfIntervals = (ulong)pitches.Length - 1; // calculate ratio of extreme intervals which exceed the requested max interval extremeIntervalMetric = (totalNumOfIntervals - numOfExtremeIntervals) / (float)totalNumOfIntervals; // calculate metric of overal deviation from max interval overallDeviationMetric = (numOfExtremeIntervals == 0) ? 1 : numOfExtremeIntervals / overallDeviation; // return weighted fitness according to the two different metrics return((0.4 * extremeIntervalMetric) + (0.6 * overallDeviationMetric)); }
/// <summary> /// Evaluates how "smooth" the movement is from tone to another. /// <para> This evaluation is based on the intervals between consecutive notes /// and the notes' degrees in context of their underlying chord. /// According to the interval type (perfect, consonants, dissonant) and the /// note's degree (chord note, diatonic note, etc) a weighted fitness is set. </para> /// </summary> /// <param name="candidate"> The melody candidate to evaluate. </param> /// <returns> The fitness outcome score for the requested evaluation. </returns> private protected double EvaluateSmoothMovement(MelodyCandidate candidate, PitchInterval maxInterval = PitchInterval.PerfectFifth) { // initialize counters ulong totalNumOfIntervals = 0; ulong numOfDiatonicSteps = 0; ulong numOfChordSteps = 0; ulong numOfDissonants = 0; ulong numOfPerfectConsonants = 0; ulong numOfImperfectConsonants = 0; ulong numOfTritones = 0; ulong numOfBigLeaps = 0; int adjacentDistance = 0; // initialize metrics double diatonicStepsMetric; double chordStepsMetric; double intervalTypeMetric; // additional initializtions double fitness = 0; double factor = 0.5; // factor for balancing this fitness function PitchInterval interval; int maxDistance = (int)maxInterval; // retrieve and filter all pitches which are not hold/rest notes NotePitch[] pitches = candidate.Bars.GetAllPitches().ToArray(); // accumulate extreme adjacent notes pitch distance for (int i = 0; i < pitches.Length - 1; i++) { // calculate pitch distance between the two adjacent notes adjacentDistance = Math.Abs(pitches[i + 1] - pitches[i]); // update relevant counters according to step size Enum.TryParse(adjacentDistance.ToString(), out interval); switch (interval) { case PitchInterval.Unison: numOfPerfectConsonants++; break; case PitchInterval.MinorSecond: case PitchInterval.MajorSecond: numOfDissonants++; numOfDiatonicSteps++; break; case PitchInterval.MinorThird: case PitchInterval.MajorThird: numOfChordSteps++; numOfImperfectConsonants++; break; case PitchInterval.PerfectFourth: numOfPerfectConsonants++; break; case PitchInterval.Tritone: numOfTritones++; break; case PitchInterval.PerfectFifth: numOfChordSteps++; numOfPerfectConsonants++; break; case PitchInterval.MinorSixth: case PitchInterval.MajorSixth: numOfImperfectConsonants++; break; case PitchInterval.MinorSeventh: case PitchInterval.MajorSeventh: numOfDissonants++; break; case PitchInterval.Octave: numOfChordSteps++; numOfPerfectConsonants++; break; default: numOfBigLeaps++; break; } } // set total number of intervals totalNumOfIntervals = (ulong)pitches.Length - 1; // calculate diatonic steps ratio metric diatonicStepsMetric = numOfDiatonicSteps / totalNumOfIntervals; // calculate chord steps ratio metric chordStepsMetric = numOfChordSteps / totalNumOfIntervals; // calculate chord steps ratio metric with weighted sum according to interval type intervalTypeMetric = ((numOfPerfectConsonants * 1.0) + (numOfImperfectConsonants * 0.8) + (numOfDissonants * 0.6) + (numOfTritones * 0.3) + (numOfBigLeaps * 0.1)) / totalNumOfIntervals; // calculate total weighted fitness according to the different metrics fitness = (0.50 * diatonicStepsMetric) + (0.30 * chordStepsMetric) + (0.20 * intervalTypeMetric); // return fitness result return(fitness + factor); }