Beispiel #1
0
 /// <summary>
 /// Throws an exception if family is not valid.
 /// </summary>
 /// <param name="family">The family to validate.</param>
 /// <exception cref="ArgumentOutOfRangeException">The family is out-of-range.</exception>
 public static void Validate(this NoteFamily family)
 {
     if (!family.IsValid())
     {
         throw new ArgumentOutOfRangeException("NoteFamily out of range");
     }
 }
Beispiel #2
0
        /// <summary>
        /// Returns the number of semitones that note is above tonic.
        /// </summary>
        /// <param name="note">The note.</param>
        /// <param name="tonic">The tonic.</param>
        /// <returns>The number of semitones, always in [0..11].</returns>
        public static int SemitonesAbove(this Note note, NoteFamily tonic)
        {
            int result = ((int)note % 12 - (int)tonic);

            if (result < 0)
            {
                result += 12;
            }
            return(result);
        }
Beispiel #3
0
 /// <summary>
 /// Returns a copy of family which is made valid by wrapping if necessary.
 /// </summary>
 /// <param name="family">The family to wrap.</param>
 /// <returns>The wrapped copy of family.</returns>
 public static NoteFamily Wrapped(this NoteFamily family)
 {
     if ((int)family < 0)
     {
         return((NoteFamily)(11 - ((-(int)family - 1) % 12)));
     }
     else
     {
         return((NoteFamily)((int)family % 12));
     }
 }
Beispiel #4
0
 /// <summary>
 /// Constructs a chord from its root note, pattern, and inversion.
 /// </summary>
 /// <param name="root">The root note of the chord.</param>
 /// <param name="pattern">The chord pattern.</param>
 /// <param name="inversion">The inversion, in [0..N-1] where N is the number of notes
 /// in pattern.</param>
 /// <exception cref="ArgumentOutOfRangeException">root is invalid or inversion is out of
 /// range.</exception>
 public Chord(NoteFamily root, Pattern pattern, int inversion)
 {
     root.Validate();
     if (inversion < 0 || inversion >= pattern.SemitoneSequence.Length)
     {
         throw new ArgumentOutOfRangeException("inversion out of range.");
     }
     this.root             = root;
     this.pattern          = pattern;
     this.inversion        = inversion;
     this.invertedSequence = InvertSequence(pattern.SemitoneSequence, inversion);
 }
Beispiel #5
0
 /// <summary>
 /// Constructs a scale from its tonic and its pattern.
 /// </summary>
 /// <param name="tonic"></param>
 /// <param name="pattern"></param>
 public Scale(NoteFamily tonic, Pattern pattern)
 {
     this.tonic         = tonic;
     this.pattern       = pattern;
     this.ascendingMask =
         new bool[13] {
         true, false, false, false, false, false, false, false, false, false,
         false, false, true
     };
     this.descendingMask =
         new bool[13] {
         true, false, false, false, false, false, false, false, false, false,
         false, false, true
     };
     if (!ComputeMasks())
     {
         throw new ArgumentException("Invalid pattern.");
     }
 }
Beispiel #6
0
 /// <summary>
 /// Returns the number of semitones that note is above tonic.
 /// </summary>
 /// <param name="note">The note.</param>
 /// <param name="tonic">The tonic.</param>
 /// <returns>The number of semitones, always in [0..11].</returns>
 public static int SemitonesAbove(this Note note, NoteFamily tonic)
 {
     int result = ((int)note % 12 - (int)tonic);
     if (result < 0)
     {
         result += 12;
     }
     return result;
 }
Beispiel #7
0
 /// <summary>
 /// Constructs a chord from its root note, pattern, and inversion.
 /// </summary>
 /// <param name="root">The root note of the chord.</param>
 /// <param name="pattern">The chord pattern.</param>
 /// <param name="inversion">The inversion, in [0..N-1] where N is the number of notes
 /// in pattern.</param>
 /// <exception cref="ArgumentOutOfRangeException">root is invalid or inversion is out of
 /// range.</exception>
 public Chord(NoteFamily root, Pattern pattern, int inversion)
 {
     root.Validate();
     if (inversion < 0 || inversion >= pattern.SemitoneSequence.Length)
     {
         throw new ArgumentOutOfRangeException("inversion out of range.");
     }
     this.root = root;
     this.pattern = pattern;
     this.inversion = inversion;
     this.invertedSequence = InvertSequence(pattern.SemitoneSequence, inversion);
 }
Beispiel #8
0
 /// <summary>
 /// Returns the human-readable name of a note family.
 /// </summary>
 /// <param name="family">The family.</param>
 /// <exception cref="ArgumentOutOfRangeException">The family is out-of-range.</exception>
 public static string Name(this NoteFamily family)
 {
     family.Validate();
     return(NoteFamilyNames[(int)family]);
 }
Beispiel #9
0
 /// <summary>
 /// Returns true if the specified note family is valid.
 /// </summary>
 /// <param name="family">The family to test.</param>
 public static bool IsValid(this NoteFamily family)
 {
     return((int)family >= 0 && (int)family < 12);
 }
Beispiel #10
0
        /// <summary>
        /// Fills ascendingMask and descendingMask based on ascendingDescendingPattern.
        /// </summary>
        /// <returns>True if the operation succeeded, false if the pattern was invalid.</returns>
        private bool ComputeMasks()
        {
            int[] semitoneSequence = pattern.SemitoneSequence;
            int numAscendingNotes = 2;
            int numDescendingNotes = 2;

            // First make sure it's non-empty and starts at zero.
            if (semitoneSequence == null || semitoneSequence.Length == 0 ||
                semitoneSequence[0] != 0)
            {
                return false;
            }
            // Now run through the rest of the pattern and make sure it ascends and then descends,
            // and populate the masks as we go.
            bool ascending = true;
            for (int i = 1; i < semitoneSequence.Length; ++i)
            {
                if (ascending)
                {
                    // Make sure we've just gone up.
                    if (semitoneSequence[i] <= semitoneSequence[i - 1])
                    {
                        return false;
                    }
                    // Make sure we haven't gone up too far.
                    if (semitoneSequence[i] > 12)
                    {
                        return false;
                    }
                    // If we've reached 12, start descending, otherwise add this to the ascending
                    // mask.
                    if (semitoneSequence[i] == 12)
                    {
                        ascending = false;
                    }
                    else
                    {
                        // Add this to the ascending mask.
                        ascendingMask[semitoneSequence[i]] = true;
                        numAscendingNotes++;
                    }
                }
                else
                {
                    // Make sure we've just gone down.
                    if (semitoneSequence[i] >= semitoneSequence[i - 1])
                    {
                        return false;
                    }
                    // Make sure we haven't gone down too far.
                    if (semitoneSequence[i] < 0)
                    {
                        return false;
                    }
                    // If we've reached 0, make sure we're the last element, otherwise add this
                    // to the mask.
                    if (semitoneSequence[i] == 0)
                    {
                        if (i != semitoneSequence.Length - 1)
                        {
                            return false;
                        }
                    }
                    else
                    {
                        // Add this to the descending mask.
                        descendingMask[semitoneSequence[i]] = true;
                        numDescendingNotes++;
                    }
                }
            }

            ascendingNotes = new NoteFamily[numAscendingNotes];
            descendingNotes = new NoteFamily[numDescendingNotes];

            int numAdded = 0;
            for (int i = 0; i <= 12; ++i)
            {
                if (ascendingMask[i])
                {
                    ascendingNotes[numAdded++] = (NoteFamily)(tonic + i).Wrapped();
                }
            }

            numAdded = 0;
            for (int i = 12; i >= 0; --i)
            {
                if (descendingMask[i])
                {
                    descendingNotes[numAdded++] = (NoteFamily)(tonic + i).Wrapped();
                }
            }

            return true;
        }
Beispiel #11
0
 /// <summary>
 /// Constructs a scale from its tonic and its pattern.
 /// </summary>
 /// <param name="tonic"></param>
 /// <param name="pattern"></param>
 public Scale(NoteFamily tonic, Pattern pattern)
 {
     this.tonic = tonic;
     this.pattern = pattern;
     this.ascendingMask =
         new bool[13] { true, false, false, false, false, false, false, false, false, false,
             false, false, true };
     this.descendingMask =
         new bool[13] { true, false, false, false, false, false, false, false, false, false,
             false, false, true };
     if (!ComputeMasks())
     {
         throw new ArgumentException("Invalid pattern.");
     }
 }
Beispiel #12
0
        /// <summary>
        /// Fills ascendingMask and descendingMask based on ascendingDescendingPattern.
        /// </summary>
        /// <returns>True if the operation succeeded, false if the pattern was invalid.</returns>
        private bool ComputeMasks()
        {
            int[] semitoneSequence   = pattern.SemitoneSequence;
            int   numAscendingNotes  = 2;
            int   numDescendingNotes = 2;

            // First make sure it's non-empty and starts at zero.
            if (semitoneSequence == null || semitoneSequence.Length == 0 ||
                semitoneSequence[0] != 0)
            {
                return(false);
            }
            // Now run through the rest of the pattern and make sure it ascends and then descends,
            // and populate the masks as we go.
            bool ascending = true;

            for (int i = 1; i < semitoneSequence.Length; ++i)
            {
                if (ascending)
                {
                    // Make sure we've just gone up.
                    if (semitoneSequence[i] <= semitoneSequence[i - 1])
                    {
                        return(false);
                    }
                    // Make sure we haven't gone up too far.
                    if (semitoneSequence[i] > 12)
                    {
                        return(false);
                    }
                    // If we've reached 12, start descending, otherwise add this to the ascending
                    // mask.
                    if (semitoneSequence[i] == 12)
                    {
                        ascending = false;
                    }
                    else
                    {
                        // Add this to the ascending mask.
                        ascendingMask[semitoneSequence[i]] = true;
                        numAscendingNotes++;
                    }
                }
                else
                {
                    // Make sure we've just gone down.
                    if (semitoneSequence[i] >= semitoneSequence[i - 1])
                    {
                        return(false);
                    }
                    // Make sure we haven't gone down too far.
                    if (semitoneSequence[i] < 0)
                    {
                        return(false);
                    }
                    // If we've reached 0, make sure we're the last element, otherwise add this
                    // to the mask.
                    if (semitoneSequence[i] == 0)
                    {
                        if (i != semitoneSequence.Length - 1)
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        // Add this to the descending mask.
                        descendingMask[semitoneSequence[i]] = true;
                        numDescendingNotes++;
                    }
                }
            }

            ascendingNotes  = new NoteFamily[numAscendingNotes];
            descendingNotes = new NoteFamily[numDescendingNotes];

            int numAdded = 0;

            for (int i = 0; i <= 12; ++i)
            {
                if (ascendingMask[i])
                {
                    ascendingNotes[numAdded++] = (NoteFamily)(tonic + i).Wrapped();
                }
            }

            numAdded = 0;
            for (int i = 12; i >= 0; --i)
            {
                if (descendingMask[i])
                {
                    descendingNotes[numAdded++] = (NoteFamily)(tonic + i).Wrapped();
                }
            }

            return(true);
        }