/// <summary> /// Gets an instance of the <see cref="Interval"/> by the specified interval quality and number. /// </summary> /// <param name="intervalQuality">Interval quality.</param> /// <param name="intervalNumber">Interval number.</param> /// <returns>An instance of the <see cref="Interval"/> which represents <paramref name="intervalNumber"/> /// along with <paramref name="intervalQuality"/>.</returns> /// <exception cref="InvalidEnumArgumentException"><paramref name="intervalQuality"/> specified an /// invalid value.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="intervalNumber"/> is less than 1.</exception> /// <exception cref="ArgumentException"><paramref name="intervalQuality"/> is not applicable to /// <paramref name="intervalNumber"/>.</exception> public static Interval Get(IntervalQuality intervalQuality, int intervalNumber) { ThrowIfArgument.IsInvalidEnumValue(nameof(intervalQuality), intervalQuality); ThrowIfArgument.IsLessThan(nameof(intervalNumber), intervalNumber, 1, "Interval number is less than 1."); if (!IsQualityApplicable(intervalQuality, intervalNumber)) { throw new ArgumentException($"{intervalQuality} quality is not applicable to interval number of {intervalNumber}.", nameof(intervalQuality)); } var maxIntervalNumber = 8; if (intervalQuality == IntervalQuality.Minor || intervalQuality == IntervalQuality.Major || intervalQuality == IntervalQuality.Augmented) { maxIntervalNumber = 7; } var result = intervalNumber > maxIntervalNumber ? ((intervalNumber - 1) / 7) * Octave.OctaveSize : 0; var additionalNumber = intervalNumber; if (intervalNumber > maxIntervalNumber) { additionalNumber = ((intervalNumber - 1) % 7) + 1; } var halfTones = IntervalsHalfTones[intervalQuality]; result += halfTones[additionalNumber]; return(FromHalfSteps(result)); }
/// <summary> /// Initializes a new instance of the <see cref="IntervalDefinition"/> with the specified /// interval number and quality. /// </summary> /// <param name="number">Interval number.</param> /// <param name="quality">Interval quality.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="number"/> is less than 1.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="quality"/> specified an invalid value.</exception> public IntervalDefinition(int number, IntervalQuality quality) { ThrowIfArgument.IsLessThan(nameof(number), number, 1, "Interval number is less than 1."); ThrowIfArgument.IsInvalidEnumValue(nameof(quality), quality); Number = number; Quality = quality; }
/// <summary> /// Gets a value indicating whether the specified interval number (1 and greater) is perfect or not. /// </summary> /// <param name="intervalNumber">Interval number to determine whether it's perfect or not.</param> /// <returns>true if <paramref name="intervalNumber"/> is perfect; otherwise, false.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="intervalNumber"/> is less than 1.</exception> public static bool IsPerfect(int intervalNumber) { ThrowIfArgument.IsLessThan(nameof(intervalNumber), intervalNumber, 1, "Interval number is less than 1."); var remainder = intervalNumber % 7 - 1; return(remainder == 0 || remainder == 3 || remainder == 4); }
/// <summary> /// Initializes a new instance of the <see cref="MidiClock"/> with the specified /// value indicating whether first tick should be generated immediately after clock started, and /// tick generator. /// </summary> /// <param name="startImmediately">A value indicating whether first tick should be generated /// immediately after clock started.</param> /// <param name="tickGenerator">Tick generator used as timer firing at the specified interval. Null for /// no tick generator.</param> /// <param name="interval">Interval of clock's ticiking.</param> public MidiClock(bool startImmediately, TickGenerator tickGenerator, TimeSpan interval) { ThrowIfArgument.IsLessThan(nameof(interval), interval, TimeSpan.FromMilliseconds(1), "Interval is less than 1 ms."); _startImmediately = startImmediately; _tickGenerator = tickGenerator; if (_tickGenerator != null) { _tickGenerator.TickGenerated += OnTickGenerated; } Interval = interval; }
/// <summary> /// Gets a value indicating whether quality is applicable to the specified interval number (1 and greater) or not. /// </summary> /// <param name="intervalQuality">Interval quality to check whether it's applicable to /// <paramref name="intervalNumber"/> or not.</param> /// <param name="intervalNumber">Interval number to check whether <paramref name="intervalQuality"/> is /// applicable to it or not.</param> /// <returns>true if <paramref name="intervalQuality"/> is applicable to <paramref name="intervalNumber"/>; /// otherwise, false.</returns> /// <exception cref="InvalidEnumArgumentException"><paramref name="intervalQuality"/> specified an /// invalid value.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="intervalNumber"/> is less than 1.</exception> public static bool IsQualityApplicable(IntervalQuality intervalQuality, int intervalNumber) { ThrowIfArgument.IsInvalidEnumValue(nameof(intervalQuality), intervalQuality); ThrowIfArgument.IsLessThan(nameof(intervalNumber), intervalNumber, 1, "Interval number is less than 1."); switch (intervalQuality) { case IntervalQuality.Perfect: return(IsPerfect(intervalNumber)); case IntervalQuality.Minor: case IntervalQuality.Major: return(!IsPerfect(intervalNumber)); case IntervalQuality.Diminished: return(intervalNumber >= 2); case IntervalQuality.Augmented: return(true); } return(false); }