/// <summary> /// Starts a tick generator. /// </summary> /// <param name="interval">Interval between ticks.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is out of /// [<see cref="MinInterval"/>; <see cref="MaxInterval"/>] range.</exception> protected override void Start(TimeSpan interval) { ThrowIfArgument.IsOutOfRange(nameof(interval), interval, MinInterval, MaxInterval, $"Interval is out of [{MinInterval}, {MaxInterval}] range."); var intervalInMilliseconds = (uint)interval.TotalMilliseconds; var timeCaps = default(MidiTimerWinApi.TIMECAPS); ProcessMmResult(MidiTimerWinApi.timeGetDevCaps(ref timeCaps, (uint)Marshal.SizeOf(timeCaps))); _resolution = Math.Min(Math.Max(timeCaps.wPeriodMin, intervalInMilliseconds), timeCaps.wPeriodMax); _tickCallback = OnTick; ProcessMmResult(MidiTimerWinApi.timeBeginPeriod(_resolution)); _timerId = MidiTimerWinApi.timeSetEvent(intervalInMilliseconds, _resolution, _tickCallback, IntPtr.Zero, MidiTimerWinApi.TIME_PERIODIC); if (_timerId == NoTimerId) { var errorCode = Marshal.GetLastWin32Error(); throw new MidiDeviceException("Unable to start tick generator.", new Win32Exception(errorCode)); } }
/// <summary> /// Splits objects by the specified ratio of an object's length measuring it from /// the object's start or end. For example, 0.5 means splitting at the center of an object. /// </summary> /// <param name="objects">Objects to split.</param> /// <param name="ratio">Ratio of an object's length to split by. Valid values are from 0 to 1.</param> /// <param name="lengthType">The type an object's length should be processed according to.</param> /// <param name="from">Point of an object distance should be measured from.</param> /// <param name="tempoMap">Tempo map used for distances calculations.</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="ratio"/> is out of valid range.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value. -or- /// <paramref name="from"/> specified an invalid value.</exception> public IEnumerable <TObject> SplitAtDistance(IEnumerable <TObject> objects, double ratio, TimeSpanType lengthType, LengthedObjectTarget from, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(objects), objects); ThrowIfArgument.IsOutOfRange(nameof(ratio), ratio, ZeroRatio, FullLengthRatio, $"Ratio is out of [{ZeroRatio}; {FullLengthRatio}] range."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsInvalidEnumValue(nameof(from), from); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); foreach (var obj in objects) { if (obj == null) { yield return(default(TObject)); continue; } var distance = obj.LengthAs(lengthType, tempoMap).Multiply(ratio); var parts = SplitObjectAtDistance(obj, distance, from, tempoMap); if (parts.LeftPart != null) { yield return(parts.LeftPart); } if (parts.RightPart != null) { yield return(parts.RightPart); } } }
/// <summary> /// Initializes a new instance of the <see cref="HighPrecisionTickGenerator"/> with the specified /// interval. /// </summary> /// <param name="interval">Interval of ticking.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is out of valid range.</exception> public HighPrecisionTickGenerator(TimeSpan interval) { ThrowIfArgument.IsOutOfRange(nameof(interval), interval, MinInterval, MaxInterval, $"Interval is out of [{MinInterval}, {MaxInterval}] range."); _interval = (uint)interval.TotalMilliseconds; }
/// <summary> /// Returns an <see cref="Interval"/> by the specified signed number of /// half steps where negative one means downward interval. /// </summary> /// <param name="halfSteps">The number of half steps.</param> /// <returns>An <see cref="Interval"/> represented by the <paramref name="halfSteps"/>.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="halfSteps"/> is out of range /// (result interval is out of the [-127,127] range).</exception> public static Interval FromHalfSteps(int halfSteps) { ThrowIfArgument.IsOutOfRange(nameof(halfSteps), halfSteps, -SevenBitNumber.MaxValue, SevenBitNumber.MaxValue, "Half steps number is out of range."); return(Get((SevenBitNumber)Math.Abs(halfSteps), Math.Sign(halfSteps) < 0 ? IntervalDirection.Down : IntervalDirection.Up)); }
/// <summary> /// Initializes a new instance of the <see cref="RegularPrecisionTickGenerator"/> with the specified /// interval. /// </summary> /// <param name="interval">Interval of ticking.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is out of valid range.</exception> public RegularPrecisionTickGenerator(TimeSpan interval) { ThrowIfArgument.IsOutOfRange(nameof(interval), interval, MinInterval, MaxInterval, $"Interval is out of [{MinInterval}, {MaxInterval}] range."); _timer = new Timer(interval.TotalMilliseconds); _timer.Elapsed += OnElapsed; }
/// <summary> /// Moves to the nth anchor. /// </summary> /// <param name="index">Index of an anchor to move to.</param> /// <returns>The current <see cref="PatternBuilder"/>.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception> public PatternBuilder MoveToNthAnchor(int index) { var counter = GetAnchorCounter(null); ThrowIfArgument.IsOutOfRange(nameof(index), index, 0, counter - 1, "Index is out of range."); return(AddAction(new MoveToAnchorAction(AnchorPosition.Nth, index))); }
/// <summary> /// Starts a tick generator. /// </summary> /// <param name="interval">Interval between ticks.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="interval"/> is out of /// [<see cref="MinInterval"/>; <see cref="MaxInterval"/>] range.</exception> protected override void Start(TimeSpan interval) { ThrowIfArgument.IsOutOfRange(nameof(interval), interval, MinInterval, MaxInterval, $"Interval is out of [{MinInterval}, {MaxInterval}] range."); _timer = new Timer(interval.TotalMilliseconds); _timer.Elapsed += OnElapsed; _timer.Start(); }
/// <summary> /// Converts sub-array of the specified bytes to an instance of the <see cref="MidiEvent"/>. /// </summary> /// <param name="bytes">Bytes to take sub-array from.</param> /// <param name="offset">Offset of sub-array to read MIDI event from.</param> /// <param name="length">Length of sub-array to read MIDI event from.</param> /// <returns><see cref="MidiEvent"/> read from <paramref name="bytes"/> starting from /// <paramref name="offset"/> and taking <paramref name="length"/> of bytes.</returns> /// <exception cref="ArgumentNullException"><paramref name="bytes"/> is null.</exception> /// <exception cref="ArgumentException"><paramref name="bytes"/> is an empty array.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> is out of range. -or- /// <paramref name="length"/> is out of range.</exception> public MidiEvent Convert(byte[] bytes, int offset, int length) { ThrowIfArgument.IsNull(nameof(bytes), bytes); ThrowIfArgument.IsEmptyCollection(nameof(bytes), bytes, "Bytes is empty array."); ThrowIfArgument.IsOutOfRange(nameof(offset), offset, 0, bytes.Length - 1, "Offset is out of range."); ThrowIfArgument.IsOutOfRange(nameof(length), length, 0, bytes.Length - offset, "Length is out of range."); var dataBytes = new byte[bytes.Length - 1 - offset]; Array.Copy(bytes, offset + 1, dataBytes, 0, dataBytes.Length); return(Convert(bytes[offset], dataBytes)); }
/// <summary> /// Splits notes contained in the specified <see cref="TrackChunk"/> by the specified ratio of a /// note's length measuring it from the note's start or end. For example, 0.5 means splitting /// at the center of a note. /// </summary> /// <param name="trackChunk"><see cref="TrackChunk"/> to split notes in.</param> /// <param name="ratio">Ratio of a note's length to split by. Valid values are from 0 to 1.</param> /// <param name="lengthType">The type a note's length should be processed according to.</param> /// <param name="from">Point of a note distance should be measured from.</param> /// <param name="tempoMap">Tempo map used for distances calculations.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="ratio"/> is out of valid range.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value. -or- /// <paramref name="from"/> specified an invalid value.</exception> public static void SplitNotesAtDistance(this TrackChunk trackChunk, double ratio, TimeSpanType lengthType, LengthedObjectTarget from, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsOutOfRange(nameof(ratio), ratio, LengthedObjectsSplitter <Note> .ZeroRatio, LengthedObjectsSplitter <Note> .FullLengthRatio, $"Ratio is out of [{LengthedObjectsSplitter<Note>.ZeroRatio}; {LengthedObjectsSplitter<Note>.FullLengthRatio}] range."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsInvalidEnumValue(nameof(from), from); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); SplitTrackChunkNotes(trackChunk, (splitter, notes) => splitter.SplitAtDistance(notes, ratio, lengthType, from, tempoMap)); }
/// <summary> /// Splits chords contained in the specified <see cref="TrackChunk"/> by the specified ratio of a /// chord's length measuring it from the chord's start or end. For example, 0.5 means splitting /// at the center of a chord. /// </summary> /// <param name="trackChunk"><see cref="TrackChunk"/> to split chords in.</param> /// <param name="ratio">Ratio of a chord's length to split by. Valid values are from 0 to 1.</param> /// <param name="lengthType">The type a chord's length should be processed according to.</param> /// <param name="from">Point of a chord distance should be measured from.</param> /// <param name="tempoMap">Tempo map used for distances calculations.</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="ratio"/> is out of valid range.</exception> /// <exception cref="InvalidEnumArgumentException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="lengthType"/> specified an invalid value.</description> /// </item> /// <item> /// <description><paramref name="from"/> specified an invalid value.</description> /// </item> /// </list> /// </exception> public static void SplitChordsAtDistance(this TrackChunk trackChunk, double ratio, TimeSpanType lengthType, LengthedObjectTarget from, TempoMap tempoMap, ChordDetectionSettings settings = null) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsOutOfRange(nameof(ratio), ratio, LengthedObjectsSplitter <Chord> .ZeroRatio, LengthedObjectsSplitter <Chord> .FullLengthRatio, $"Ratio is out of [{LengthedObjectsSplitter<Chord>.ZeroRatio}; {LengthedObjectsSplitter<Chord>.FullLengthRatio}] range."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsInvalidEnumValue(nameof(from), from); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); SplitTrackChunkChords(trackChunk, (splitter, chords) => splitter.SplitAtDistance(chords, ratio, lengthType, from, tempoMap), settings); }
/// <summary> /// Splits notes contained in the specified <see cref="MidiFile"/> by the specified ratio of a /// note's length measuring it from the note's start or end. For example, 0.5 means splitting /// at the center of a note. /// </summary> /// <param name="midiFile"><see cref="MidiFile"/> to split notes in.</param> /// <param name="ratio">Ratio of a note's length to split by. Valid values are from 0 to 1.</param> /// <param name="lengthType">The type a note's length should be processed according to.</param> /// <param name="from">Point of a note distance should be measured from.</param> /// <exception cref="ArgumentNullException"><paramref name="midiFile"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="ratio"/> is out of valid range.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value. -or- /// <paramref name="from"/> specified an invalid value.</exception> public static void SplitNotesAtDistance(this MidiFile midiFile, double ratio, TimeSpanType lengthType, LengthedObjectTarget from) { ThrowIfArgument.IsNull(nameof(midiFile), midiFile); ThrowIfArgument.IsOutOfRange(nameof(ratio), ratio, LengthedObjectsSplitter <Note> .ZeroRatio, LengthedObjectsSplitter <Note> .FullLengthRatio, $"Ratio is out of [{LengthedObjectsSplitter<Note>.ZeroRatio}; {LengthedObjectsSplitter<Note>.FullLengthRatio}] range."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsInvalidEnumValue(nameof(from), from); var tempoMap = midiFile.GetTempoMap(); midiFile.GetTrackChunks().SplitNotesAtDistance(ratio, lengthType, from, tempoMap); }
/// <summary> /// Gets an octave by the specified octave number. /// </summary> /// <param name="octaveNumber">The number of an octave.</param> /// <returns>An octave with the specified number.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="octaveNumber"/> is out of valid range.</exception> public static Octave Get(int octaveNumber) { ThrowIfArgument.IsOutOfRange(nameof(octaveNumber), octaveNumber, MinOctaveNumber, MaxOctaveNumber, $"Octave number is out of [{MinOctaveNumber}, {MaxOctaveNumber}] range."); Octave octave; if (!_cache.TryGetValue(octaveNumber, out octave)) { _cache.Add(octaveNumber, octave = new Octave(octaveNumber)); } return(octave); }
/// <summary> /// Splits notes contained in the specified collection of <see cref="TrackChunk"/> by the /// specified ratio of a note's length measuring it from the note's start or end. /// For example, 0.5 means splitting at the center of a note. /// </summary> /// <param name="trackChunks">Collection of <see cref="TrackChunk"/> to split notes in.</param> /// <param name="ratio">Ratio of a note's length to split by. Valid values are from 0 to 1.</param> /// <param name="lengthType">The type a note's length should be processed according to.</param> /// <param name="from">Point of a note distance should be measured from.</param> /// <param name="tempoMap">Tempo map used for distances calculations.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunks"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="ratio"/> is out of valid range.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value. -or- /// <paramref name="from"/> specified an invalid value.</exception> public static void SplitNotesAtDistance(this IEnumerable <TrackChunk> trackChunks, double ratio, TimeSpanType lengthType, LengthedObjectTarget from, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(trackChunks), trackChunks); ThrowIfArgument.IsOutOfRange(nameof(ratio), ratio, LengthedObjectsSplitter <Note> .ZeroRatio, LengthedObjectsSplitter <Note> .FullLengthRatio, $"Ratio is out of [{LengthedObjectsSplitter<Note>.ZeroRatio}; {LengthedObjectsSplitter<Note>.FullLengthRatio}] range."); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsInvalidEnumValue(nameof(from), from); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); foreach (var trackChunk in trackChunks) { trackChunk.SplitNotesAtDistance(ratio, lengthType, from, tempoMap); } }
/// <summary> /// Gets an octave definition by the specified octave number. /// </summary> /// <param name="octave">The number of an octave.</param> /// <returns>A definition of the octave with the specified number.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="octave"/> is out of valid range.</exception> public static OctaveDefinition Get(int octave) { ThrowIfArgument.IsOutOfRange(nameof(octave), octave, MinOctaveNumber, MaxOctaveNumber, $"Octave number is out of [{MinOctaveNumber}, {MaxOctaveNumber}] range."); OctaveDefinition octaveDefinition; if (!_cache.TryGetValue(octave, out octaveDefinition)) { _cache.Add(octave, octaveDefinition = new OctaveDefinition(octave)); } return(octaveDefinition); }
/// <summary> /// Retrieves output MIDI device with the specified ID. /// </summary> /// <param name="id">Device ID which is number from 0 to <see cref="GetDevicesCount"/> minus 1.</param> /// <returns>Output MIDI device with the specified ID.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="id"/> is out of valid range.</exception> public static OutputDevice GetById(int id) { ThrowIfArgument.IsOutOfRange(nameof(id), id, 0, GetDevicesCount() - 1, "Device ID is out of range."); return(new OutputDevice(id)); }
/// <summary> /// Initializes a new instance of the <see cref="SevenBitNumber"/> with the specified value. /// </summary> /// <param name="value">Value representing seven-bit number.</param> /// <exception cref="ArgumentOutOfRangeException"><paramref name="value"/> is out of /// [<see cref="MinValue"/>; <see cref="MaxValue"/>] range.</exception> public SevenBitNumber(byte value) { ThrowIfArgument.IsOutOfRange(nameof(value), value, Min, Max, "Value is out of range valid for seven-bit number."); _value = value; }