/// <summary> /// Shrinks the current time span by dividing its length by the specified divisor. /// </summary> /// <param name="divisor">Divisor to shrink the time span by.</param> /// <returns>Time span that is the current time span shrinked by the <paramref name="divisor"/>.</returns> public ITimeSpan Divide(double divisor) { ThrowIfArgument.IsNonpositive(nameof(divisor), divisor, "Divisor is zero or negative."); return(new MusicalTimeSpan(Numerator * FractionPartMultiplier, MathUtilities.RoundToLong(Denominator * MathUtilities.Round(divisor, NumberOfDigitsAfterDecimalPoint) * FractionPartMultiplier))); }
/// <summary> /// Shrinks the current time span by dividing its length by the specified divisor. /// </summary> /// <param name="divisor">Divisor to shrink the time span by.</param> /// <returns>Time span that is the current time span shrinked by the <paramref name="divisor"/>.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="divisor"/> is zero or negative.</exception> public ITimeSpan Divide(double divisor) { ThrowIfArgument.IsNonpositive(nameof(divisor), divisor, "Divisor is zero or negative."); return(new BarBeatFractionTimeSpan(MathUtilities.RoundToLong(Bars / divisor), Beats / divisor)); }
/// <summary> /// Creates an instance of the <see cref="Tempo"/> with the specified number of /// beats per minute. /// </summary> /// <param name="beatsPerMinute">Number of beats per minute.</param> /// <returns>An instance of the <see cref="Tempo"/> which represents tempo as specified /// number of beats per minute.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="beatsPerMinute"/> /// is zero or negative.</exception> public static Tempo FromBeatsPerMinute(int beatsPerMinute) { ThrowIfArgument.IsNonpositive(nameof(beatsPerMinute), beatsPerMinute, "Number of beats per minute is zero or negative."); return(new Tempo(MathUtilities.RoundToLong((double)MicrosecondsInMinute / beatsPerMinute))); }
/// <summary> /// Creates an instance of the <see cref="Tempo"/> with the specified number of /// milliseconds per quarter note. /// </summary> /// <param name="millisecondsPerQuarterNote">Number of milliseconds per quarter note.</param> /// <returns>An instance of the <see cref="Tempo"/> which represents tempo as specified /// number of milliseconds per quarter note.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsPerQuarterNote"/> /// is zero or negative.</exception> public static Tempo FromMillisecondsPerQuarterNote(long millisecondsPerQuarterNote) { ThrowIfArgument.IsNonpositive(nameof(millisecondsPerQuarterNote), millisecondsPerQuarterNote, "Number of milliseconds per quarter note is zero or negative."); return(new Tempo(millisecondsPerQuarterNote * MicrosecondsInMillisecond)); }
/// <summary> /// Initializes a new instance of the <see cref="Tempo"/> with the specified number of /// microseconds per quarter note. /// </summary> /// <param name="microsecondsPerQuarterNote">Number of microseconds per quarter note.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="microsecondsPerQuarterNote"/> /// is zero or negative.</exception> public Tempo(long microsecondsPerQuarterNote) { ThrowIfArgument.IsNonpositive(nameof(microsecondsPerQuarterNote), microsecondsPerQuarterNote, "Number of microseconds per quarter note is zero or negative."); MicrosecondsPerQuarterNote = microsecondsPerQuarterNote; }
/// <summary> /// Divides the specified <see cref="MusicalTimeSpan"/> by a number. /// </summary> /// <param name="timeSpan">The <see cref="MusicalTimeSpan"/> to divide by <paramref name="number"/>.</param> /// <param name="number">The multiplier.</param> /// <returns>An object whose value is the result of division of <paramref name="timeSpan"/> by /// <paramref name="number"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="timeSpan"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="number"/> is negative.</exception> public static MusicalTimeSpan operator /(MusicalTimeSpan timeSpan, long number) { ThrowIfArgument.IsNull(nameof(timeSpan), timeSpan); ThrowIfArgument.IsNonpositive(nameof(number), number, "Number is zero or negative."); return(new MusicalTimeSpan(timeSpan.Numerator, timeSpan.Denominator * number)); }
/// <summary> /// Shrinks the current time span by dividing its length by the specified divisor. /// </summary> /// <param name="divisor">Divisor to shrink the time span by.</param> /// <returns>Time span that is the current time span shrinked by the <paramref name="divisor"/>.</returns> public ITimeSpan Divide(double divisor) { ThrowIfArgument.IsNonpositive(nameof(divisor), divisor, "Divisor is zero or negative."); return(new BarBeatTimeSpan((long)Math.Round(Bars / divisor), (long)Math.Round(Beats / divisor), (long)Math.Round(Ticks / divisor))); }
/// <summary> /// Returns a tuplet based on the current <see cref="MusicalTimeSpan"/>. /// </summary> /// <param name="tupletNotesCount">Notes count of a tuplet to construct.</param> /// <param name="tupletSpaceSize">Space of a tuplet to construct.</param> /// <returns>A tuplet based on the current <see cref="MusicalTimeSpan"/>.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="tupletNotesCount"/> is zero /// or negative. -or- <paramref name="tupletSpaceSize"/> is zero or negative.</exception> public MusicalTimeSpan Tuplet(int tupletNotesCount, int tupletSpaceSize) { ThrowIfArgument.IsNonpositive(nameof(tupletNotesCount), tupletNotesCount, "Tuplet's notes count is zero or negative."); ThrowIfArgument.IsNonpositive(nameof(tupletSpaceSize), tupletSpaceSize, "Tuplet's space size is zero or negative."); return(new MusicalTimeSpan(Numerator * tupletSpaceSize, Denominator * tupletNotesCount)); }
/// <summary> /// Changes denominator of the current <see cref="MusicalTimeSpan"/>. /// </summary> /// <param name="denominator">New denominator.</param> /// <returns>An instance of the <see cref="MusicalTimeSpan"/> which represents the same time span as /// the original one but with the specified denominator.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="denominator"/> is zero or negative.</exception> public MusicalTimeSpan ChangeDenominator(long denominator) { ThrowIfArgument.IsNonpositive(nameof(denominator), denominator, "Denominator is zero or negative."); var numerator = MathUtilities.RoundToLong((double)denominator / Denominator * Numerator); return(new MusicalTimeSpan(numerator, denominator, false)); }
/// <summary> /// Splits chords contained in the specified <see cref="TrackChunk"/> into the specified number /// of parts of the equal length. /// </summary> /// <remarks> /// If a chord has zero length, it will be split into the specified number of parts of zero length. /// </remarks> /// <param name="trackChunk"><see cref="TrackChunk"/> to split chords in.</param> /// <param name="partsNumber">The number of parts to split chords into.</param> /// <param name="lengthType">Type of a part's length.</param> /// <param name="tempoMap">Tempo map used to calculate times to split by.</param> /// <param name="settings">Settings accoridng to which chords should be detected and built.</param> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="trackChunk"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="partsNumber"/> is zero or negative.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public static void SplitChordsByPartsNumber(this TrackChunk trackChunk, int partsNumber, TimeSpanType lengthType, TempoMap tempoMap, ChordDetectionSettings settings = null) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsNonpositive(nameof(partsNumber), partsNumber, "Parts number is zero or negative."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); SplitTrackChunkChords(trackChunk, (splitter, chords) => splitter.SplitByPartsNumber(chords, partsNumber, lengthType, tempoMap), settings); }
/// <summary> /// Splits notes contained in the specified <see cref="TrackChunk"/> into the specified number /// of parts of the equal length. /// </summary> /// <remarks> /// If a note has zero length, it will be splitted into the specified number of parts of zero length. /// </remarks> /// <param name="trackChunk"><see cref="TrackChunk"/> to split notes in.</param> /// <param name="partsNumber">The number of parts to split notes into.</param> /// <param name="lengthType">Type of a part's length.</param> /// <param name="tempoMap">Tempo map used to calculate times to split by.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="partsNumber"/> is zero or negative.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public static void SplitNotesByPartsNumber(this TrackChunk trackChunk, int partsNumber, TimeSpanType lengthType, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsNonpositive(nameof(partsNumber), partsNumber, "Parts number is zero or negative."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); SplitTrackChunkNotes(trackChunk, (splitter, notes) => splitter.SplitByPartsNumber(notes, partsNumber, lengthType, tempoMap)); }
/// <summary> /// Splits chords contained in the specified <see cref="TrackChunk"/> into the specified number /// of parts of the equal length. /// </summary> /// <remarks> /// If a chord has zero length, it will be splitted into the specified number of parts of zero length. /// </remarks> /// <param name="trackChunk"><see cref="TrackChunk"/> to split chords in.</param> /// <param name="partsNumber">The number of parts to split chords into.</param> /// <param name="lengthType">Type of a part's length.</param> /// <param name="tempoMap">Tempo map used to calculate times to split by.</param> /// <param name="notesTolerance">Notes tolerance that defines maximum distance of notes from the /// start of the first note of a chord. Notes within this tolerance will be considered as a chord.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="partsNumber"/> is zero or negative. -or- /// <paramref name="notesTolerance"/> is negative.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public static void SplitChordsByPartsNumber(this TrackChunk trackChunk, int partsNumber, TimeSpanType lengthType, TempoMap tempoMap, long notesTolerance = 0) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsNonpositive(nameof(partsNumber), partsNumber, "Parts number is zero or negative."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); ThrowIfNotesTolerance.IsNegative(nameof(notesTolerance), notesTolerance); SplitTrackChunkChords(trackChunk, (splitter, chords) => splitter.SplitByPartsNumber(chords, partsNumber, lengthType, tempoMap), notesTolerance); }
/// <summary> /// Splits notes contained in the specified <see cref="MidiFile"/> into the specified number of /// parts of the equal length. /// </summary> /// <remarks> /// If a note has zero length, it will be splitted into the specified number of parts of zero length. /// </remarks> /// <param name="midiFile"><see cref="MidiFile"/> to split notes in.</param> /// <param name="partsNumber">The number of parts to split notes into.</param> /// <param name="lengthType">Type of a part's length.</param> /// <exception cref="ArgumentNullException"><paramref name="midiFile"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="partsNumber"/> is zero or negative.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public static void SplitNotesByPartsNumber(this MidiFile midiFile, int partsNumber, TimeSpanType lengthType) { ThrowIfArgument.IsNull(nameof(midiFile), midiFile); ThrowIfArgument.IsNonpositive(nameof(partsNumber), partsNumber, "Parts number is zero or negative."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); var tempoMap = midiFile.GetTempoMap(); midiFile.GetTrackChunks().SplitNotesByPartsNumber(partsNumber, lengthType, tempoMap); }
/// <summary> /// Initializes a new instance of the <see cref="MusicalTimeSpan"/> with the specified /// numerator and denominator of the fraction of the whole note's length. /// </summary> /// <param name="numerator">The numerator of fraction of the whole note's length.</param> /// <param name="denominator">The denominator of fraction of the whole note's length.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="numerator"/> is negative. -or- /// <paramref name="denominator"/> is zero or negative.</exception> public MusicalTimeSpan(long numerator, long denominator) { ThrowIfArgument.IsNegative(nameof(numerator), numerator, "Numerator is negative."); ThrowIfArgument.IsNonpositive(nameof(denominator), denominator, "Denominator is negative."); var greatestCommonDivisor = MathUtilities.GreatestCommonDivisor(numerator, denominator); Numerator = numerator / greatestCommonDivisor; Denominator = denominator / greatestCommonDivisor; }
/// <summary> /// Splits objects into the specified number of parts of the equal length. /// </summary> /// <remarks> /// Nulls will not be splitted and will be returned as nulls. If an object has zero length, /// it will be splitted into the specified number of parts of zero length. /// </remarks> /// <param name="objects">Objects to split.</param> /// <param name="partsNumber">The number of parts to split objects into.</param> /// <param name="lengthType">Type of a part's length.</param> /// <param name="tempoMap">Tempo map used to calculate times to split by.</param> /// <returns>Objects that are result of splitting <paramref name="objects"/> going in the same /// order as elements of <paramref name="objects"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="objects"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="partsNumber"/> is zero or negative.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public IEnumerable <TObject> SplitByPartsNumber(IEnumerable <TObject> objects, int partsNumber, TimeSpanType lengthType, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(objects), objects); ThrowIfArgument.IsNonpositive(nameof(partsNumber), partsNumber, "Parts number is zero or negative."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); foreach (var obj in objects) { if (obj == null) { yield return(default(TObject)); continue; } if (partsNumber == 1) { yield return(CloneObject(obj)); continue; } if (obj.Length == 0) { foreach (var i in Enumerable.Range(0, partsNumber)) { yield return(CloneObject(obj)); } continue; } var time = obj.Time; var tail = CloneObject(obj); for (int partsRemaining = partsNumber; partsRemaining > 1 && tail != null; partsRemaining--) { var length = tail.LengthAs(lengthType, tempoMap); var partLength = length.Divide(partsRemaining); time += LengthConverter.ConvertFrom(partLength, time, tempoMap); var parts = SplitObject(tail, time); yield return(parts.LeftPart); tail = parts.RightPart; } if (tail != null) { yield return(tail); } } }
/// <summary> /// Initializes a new instance of the <see cref="TimeSignature" /> with the specified /// numerator and denominator. /// </summary> /// <param name="numerator">Numerator of the time signature which defines number of beats.</param> /// <param name="denominator">Denominator of the time signature which defines beat length.</param> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="numerator" /> is zero or negative. -or- /// <paramref name="denominator" /> is zero or negative. -or- <paramref name="denominator" /> is not a /// power of two. /// </exception> public TimeSignature(int numerator, int denominator) { ThrowIfArgument.IsNonpositive(nameof(numerator), numerator, "Numerator is zero or negative."); ThrowIfArgument.IsNonpositive(nameof(denominator), denominator, "Denominator is zero or negative."); ThrowIfArgument.DoesntSatisfyCondition(nameof(denominator), denominator, MathUtilities.IsPowerOfTwo, "Denominator is not a power of two."); Numerator = numerator; Denominator = denominator; }
/// <summary> /// Splits notes contained in the specified collection of <see cref="TrackChunk"/> into the /// specified number of parts of the equal length. /// </summary> /// <remarks> /// If a note has zero length, it will be splitted into the specified number of parts of zero length. /// </remarks> /// <param name="trackChunks">Collection of <see cref="TrackChunk"/> to split notes in.</param> /// <param name="partsNumber">The number of parts to split notes into.</param> /// <param name="lengthType">Type of a part's length.</param> /// <param name="tempoMap">Tempo map used to calculate times to split by.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunks"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="partsNumber"/> is zero or negative.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public static void SplitNotesByPartsNumber(this IEnumerable <TrackChunk> trackChunks, int partsNumber, TimeSpanType lengthType, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(trackChunks), trackChunks); ThrowIfArgument.IsNonpositive(nameof(partsNumber), partsNumber, "Parts number is zero or negative."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); foreach (var trackChunk in trackChunks) { trackChunk.SplitNotesByPartsNumber(partsNumber, lengthType, tempoMap); } }
/// <summary> /// Creates an instance of the <see cref="Fraction"/> that represents the specified fraction /// of the whole length. Fraction can have dots and can represent tuplet's note length. /// </summary> /// <param name="fraction">Fraction of the whole length, e.g. 8 for 1/8.</param> /// <param name="dotsCount">Count of dots attached to the fraction.</param> /// <param name="tupletNotesCount">Count of notes a tuplet made up from, e.g. 3 for triplet.</param> /// <param name="tupletSpaceSize">Size of tuplet's space which is count of regular notes /// that have total length equal to the tuplet, e.g. 2 for triplet.</param> /// <returns><see cref="Fraction"/> that represents fraction of the whole length, e.g. 3/2 for dotted whole.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="fraction"/> is zero or negative. -or- /// <paramref name="dotsCount"/> is negative. -or- <paramref name="tupletNotesCount"/> is zero or /// negative. -or- <paramref name="tupletSpaceSize"/> is zero or negative.</exception> public static Fraction CreateDottedTuplet(long fraction, int dotsCount, int tupletNotesCount, int tupletSpaceSize) { ThrowIfArgument.IsNonpositive(nameof(fraction), fraction, "Fraction is zero or negative."); ThrowIfArgument.IsNegative(nameof(dotsCount), dotsCount, "Dots count is negative."); ThrowIfArgument.IsNonpositive(nameof(tupletNotesCount), tupletNotesCount, "Tuplet's notes count is zero or negative."); ThrowIfArgument.IsNonpositive(nameof(tupletSpaceSize), tupletSpaceSize, "Tuplet's space size is zero or negative."); // [1] create simple fraction: // f = 1 / fraction // // [2] add dots: // f = f * (2 ^ (dotsCount + 1) - 1) / 2 ^ dotsCount // // [3] make tuplet: // f = f * tupletSpaceSize / tupletNotesCount return(new Fraction(((1 << dotsCount + 1) - 1) * tupletSpaceSize, fraction * (1 << dotsCount) * tupletNotesCount)); }
/// <summary> /// Shrinks the current time span by dividing its length by the specified divisor. /// </summary> /// <param name="divisor">Divisor to shrink the time span by.</param> /// <returns>Time span that is the current time span shrinked by the <paramref name="divisor"/>.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="divisor"/> is zero or negative.</exception> public ITimeSpan Divide(double divisor) { ThrowIfArgument.IsNonpositive(nameof(divisor), divisor, "Divisor is zero or negative."); return(new MidiTimeSpan(MathUtilities.RoundToLong(TimeSpan / divisor))); }
/// <summary> /// Shrinks the current time span by dividing its length by the specified divisor. /// </summary> /// <param name="divisor">Divisor to shrink the time span by.</param> /// <returns>Time span that is the current time span shrinked by the <paramref name="divisor"/>.</returns> public ITimeSpan Divide(double divisor) { ThrowIfArgument.IsNonpositive(nameof(divisor), divisor, "Divisor is zero or negative."); return(new MetricTimeSpan((long)Math.Round(TotalMicroseconds / divisor))); }