From 749c7b7a1606b49c13f53b8ac25e1d1e18b16449 Mon Sep 17 00:00:00 2001 From: "Maxim K. Dobroselsky" Date: Tue, 21 Jan 2025 20:50:52 +0300 Subject: [PATCH] Track pitch value by default in Playback --- ...ybackTests.ObservableCollection.Complex.cs | 16 +- .../Playback/PlaybackTests.TrackPitchValue.cs | 820 +++++++++++++----- .../Playback/PlaybackTests.TrackProgram.cs | 9 +- .../Playback/PlaybackDataTracker.cs | 2 +- 4 files changed, 603 insertions(+), 244 deletions(-) diff --git a/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.ObservableCollection.Complex.cs b/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.ObservableCollection.Complex.cs index 5060759b1..161a9792c 100644 --- a/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.ObservableCollection.Complex.cs +++ b/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.ObservableCollection.Complex.cs @@ -16,7 +16,7 @@ public sealed partial class PlaybackTests [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceByOne( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount, + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount, [Values(0, 10)] int gapMs) { var noteLengthMs = 20; @@ -65,7 +65,7 @@ public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceByOne( [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceByOne_WithEndMovesBehindCurrentTime( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount) + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount) { var noteLengthMs = 20; var lastEventTime = notesCount * noteLengthMs + 20; @@ -116,7 +116,7 @@ public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceByOne_WithEndMovesBehin [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceByOneWithOverlapping( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount) + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount) { var overlappedMs = 5; var noteLengthMs = 20; @@ -166,7 +166,7 @@ public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceByOneWithOverlapping( [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_BatchAdd_1( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount) + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount) { var noteLengthMs = 20; var lastEventTime = notesCount * noteLengthMs + 20; @@ -268,7 +268,7 @@ public void CheckPlaybackDataChangesOnTheFly_BatchAdd_2( [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_RemoveByOne( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount) + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount) { var noteLengthMs = 20; @@ -305,7 +305,7 @@ public void CheckPlaybackDataChangesOnTheFly_RemoveByOne( [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_RemoveAtAdvance( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount) + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount) { var noteLengthMs = 40; @@ -365,7 +365,7 @@ public void CheckPlaybackDataChangesOnTheFly_ShiftNoteAtAdvance() [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_AddAndRemoveAtAdvanceByOne( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount, + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount, [Values(0, 10)] int gapMs, [Values] bool viaChangeCollection) { @@ -418,7 +418,7 @@ public void CheckPlaybackDataChangesOnTheFly_AddAndRemoveAtAdvanceByOne( [Retry(OnTheFlyChecksRetriesNumber)] [Test] public void CheckPlaybackDataChangesOnTheFly_AddAtAdvanceAndRemovePastByOne( - [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51, 64)] int notesCount) + [Values(1, 2, 3, 4, 8, 16, 17, 32, 50, 51)] int notesCount) { var noteLengthMs = 20; var lastEventTime = notesCount * noteLengthMs + 20; diff --git a/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackPitchValue.cs b/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackPitchValue.cs index a22939381..9f036d315 100644 --- a/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackPitchValue.cs +++ b/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackPitchValue.cs @@ -1,405 +1,759 @@ using System; -using System.Collections.Generic; using Melanchall.DryWetMidi.Common; using Melanchall.DryWetMidi.Core; -using Melanchall.DryWetMidi.Multimedia; +using Melanchall.DryWetMidi.Interaction; using NUnit.Framework; namespace Melanchall.DryWetMidi.Tests.Multimedia { - // TODO: check tracking disabled [TestFixture] public sealed partial class PlaybackTests { #region Test methods [Retry(RetriesNumber)] - [TestCase(true, 0)] - [TestCase(true, 100)] - [TestCase(false, 0)] - [TestCase(false, 100)] - public void TrackPitchValue_NoPitchBend_MoveToTime(bool useOutputDevice, int moveFromMs) + [Test] + public void TrackPitchValue_NoPitchBend_MoveToTime( + [Values(0, 100)] int moveFromMs, + [Values(0, 500)] int moveToMs) { - var noteOffDelay = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(1); var moveFrom = TimeSpan.FromMilliseconds(moveFromMs); + var moveTo = TimeSpan.FromMilliseconds(moveToMs); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }); + } + + [Retry(RetriesNumber)] + [Test] + public void TrackPitchValue_PitchBendAtZero_MoveToTime() + { + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(100); var moveTo = TimeSpan.FromMilliseconds(500); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new StartEvent(), noteOffDelay) + new TimedEvent(new PitchBendEvent(pitchValue)) + .SetTime((MetricTimeSpan)TimeSpan.Zero, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new StartEvent(), noteOffDelay - (moveTo - moveFrom)) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue), TimeSpan.Zero), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_NoPitchBend_MoveToStart(bool useOutputDevice) + [Test] + public void TrackPitchValue_PitchBendAtZero_MoveToStart() { - var noteOffDelay = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; var moveFrom = TimeSpan.FromMilliseconds(500); var moveTo = TimeSpan.Zero; - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new StartEvent(), noteOffDelay) + new TimedEvent(new PitchBendEvent(pitchValue)) + .SetTime((MetricTimeSpan)TimeSpan.Zero, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new StartEvent(), noteOffDelay - (moveTo - moveFrom)) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue), TimeSpan.Zero), + new ReceivedEvent(new PitchBendEvent(pitchValue), moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_PitchBendAtZero_MoveToTime(bool useOutputDevice) + [Test] + public void TrackPitchValue_FromBeforePitchBend_ToBeforePitchBend() { - var noteOffDelay = TimeSpan.FromSeconds(2); + var pitchBendTime = TimeSpan.FromMilliseconds(800); + var lastEventTime = TimeSpan.FromSeconds(1); var pitchValue = (ushort)234; var moveFrom = TimeSpan.FromMilliseconds(100); var moveTo = TimeSpan.FromMilliseconds(500); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - (moveTo - moveFrom)), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }); + } + + [Retry(RetriesNumber)] + [Test] + public void TrackPitchValue_FromBeforePitchBend_ToAfterPitchBend() + { + var pitchBendTime = TimeSpan.FromMilliseconds(500); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(300); + var moveTo = TimeSpan.FromMilliseconds(700); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue), TimeSpan.Zero), - new EventToSend(new StartEvent(), noteOffDelay) + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue), TimeSpan.Zero), - new EventToSend(new StartEvent(), noteOffDelay - (moveTo - moveFrom)) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_PitchBendAtZero_MoveToStart(bool useOutputDevice) + [Test] + public void TrackPitchValue_FromAfterPitchBend_ToAfterPitchBend() { - var noteOffDelay = TimeSpan.FromSeconds(2); + var pitchBendTime = TimeSpan.FromMilliseconds(400); + var lastEventTime = TimeSpan.FromSeconds(1); var pitchValue = (ushort)234; var moveFrom = TimeSpan.FromMilliseconds(500); - var moveTo = TimeSpan.Zero; + var moveTo = TimeSpan.FromMilliseconds(700); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }); + } + + [Retry(RetriesNumber)] + [Test] + public void TrackPitchValue_FromAfterPitchBend_ToBeforePitchBend() + { + var pitchBendTime = TimeSpan.FromMilliseconds(500); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(700); + var moveTo = TimeSpan.FromMilliseconds(300); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new PitchBendEvent() { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime + moveFrom - moveTo), + new ReceivedEvent(new StartEvent(), lastEventTime + moveFrom - moveTo), + }); + } + + [Retry(RetriesNumber)] + [Test] + public void TrackPitchValue_FromBeforePitchBend_ToPitchBend() + { + var pitchBendTime = TimeSpan.FromMilliseconds(800); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; - CheckTrackPitchValue( - eventsToSend: new[] + var moveFrom = TimeSpan.FromMilliseconds(500); + var moveTo = TimeSpan.FromMilliseconds(800); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue), TimeSpan.Zero), - new EventToSend(new StartEvent(), noteOffDelay) + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue), TimeSpan.Zero), - new EventToSend(new PitchBendEvent(pitchValue), moveFrom), - new EventToSend(new StartEvent(), noteOffDelay - moveTo) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime + moveFrom - moveTo), + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_FromBeforePitchBend_ToBeforePitchBend(bool useOutputDevice) + [Test] + public void TrackPitchValue_FromAfterPitchBend_ToPitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(1); var pitchValue = (ushort)234; - var moveFrom = TimeSpan.FromMilliseconds(100); - var moveTo = TimeSpan.FromMilliseconds(500); + var moveFrom = TimeSpan.FromMilliseconds(900); + var moveTo = TimeSpan.FromMilliseconds(800); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - (moveTo - moveFrom)), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - pitchBendTime + moveFrom), + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_FromBeforePitchBend_ToAfterPitchBend(bool useOutputDevice) + [Test] + public void TrackPitchValue_EnableInMiddle_FromBeforePitchBend_ToAfterPitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var programChangeTime = TimeSpan.FromSeconds(1); + var lastEventTime = TimeSpan.FromSeconds(2); var pitchValue = (ushort)234; + var programNumber = (SevenBitNumber)100; var moveFrom = TimeSpan.FromMilliseconds(500); - var moveTo = TimeSpan.FromMilliseconds(1000); + var moveTo = TimeSpan.FromMilliseconds(1200); + var enableAfter = TimeSpan.FromMilliseconds(500); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)programChangeTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)), + new PlaybackChangerBase(enableAfter, + p => p.TrackPitchValue = true), }, - eventsWillBeSent: new[] + expectedReceivedEvents: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), - new EventToSend(new StartEvent(), noteOffTime - (moveTo - moveFrom) - moveFrom) + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom + enableAfter), + new ReceivedEvent(new StartEvent(), lastEventTime - moveTo + moveFrom), }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + setupPlayback: playback => + { + playback.TrackProgram = false; + playback.TrackPitchValue = false; + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_FromAfterPitchBend_ToAfterPitchBend(bool useOutputDevice) + [Test] + public void TrackPitchValue_EnableInMiddle_FromAfterPitchBend_ToBeforePitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(2); var pitchValue = (ushort)234; var moveFrom = TimeSpan.FromMilliseconds(1000); - var moveTo = TimeSpan.FromMilliseconds(1500); + var moveTo = TimeSpan.FromMilliseconds(500); + var enableAfter = TimeSpan.FromMilliseconds(150); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)), + new PlaybackChangerBase(enableAfter, + p => p.TrackPitchValue = true), + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new PitchBendEvent() { Channel = (FourBitNumber)4 }, moveFrom + enableAfter), + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - moveTo + moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - moveTo + moveFrom), + }, + setupPlayback: playback => playback.TrackPitchValue = false); + } + + [Retry(RetriesNumber)] + [Test] + public void DontTrackPitchValue_NoPitchBend_MoveToTime( + [Values(0, 100)] int moveFromMs, + [Values(0, 500)] int moveToMs) + { + var lastEventTime = TimeSpan.FromSeconds(1); - CheckTrackPitchValue( - eventsToSend: new[] + var moveFrom = TimeSpan.FromMilliseconds(moveFromMs); + var moveTo = TimeSpan.FromMilliseconds(moveToMs); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime - (moveTo - moveFrom)) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }, + setupPlayback: playback => playback.TrackPitchValue = false); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_FromAfterPitchBend_ToBeforePitchBend(bool useOutputDevice) + [Test] + public void DontTrackPitchValue_PitchBendAtZero_MoveToTime() + { + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(100); + var moveTo = TimeSpan.FromMilliseconds(500); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue)) + .SetTime((MetricTimeSpan)TimeSpan.Zero, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue), TimeSpan.Zero), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }, + setupPlayback: playback => playback.TrackPitchValue = false); + } + + [Retry(RetriesNumber)] + [Test] + public void DontTrackPitchValue_PitchBendAtZero_MoveToStart() + { + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(500); + var moveTo = TimeSpan.Zero; + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue)) + .SetTime((MetricTimeSpan)TimeSpan.Zero, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue), TimeSpan.Zero), + new ReceivedEvent(new PitchBendEvent(pitchValue), moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }, + setupPlayback: playback => playback.TrackPitchValue = false); + } + + [Retry(RetriesNumber)] + [Test] + public void DontTrackPitchValue_FromBeforePitchBend_ToBeforePitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(1); var pitchValue = (ushort)234; - var moveFrom = TimeSpan.FromMilliseconds(1000); + var moveFrom = TimeSpan.FromMilliseconds(100); var moveTo = TimeSpan.FromMilliseconds(500); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - eventsWillBeSent: new[] + expectedReceivedEvents: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new PitchBendEvent() { Channel = (FourBitNumber)4 }, moveFrom - pitchBendTime), - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - moveTo), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - (moveTo - moveFrom)), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + setupPlayback: playback => playback.TrackPitchValue = false); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_FromBeforePitchBend_ToPitchBend(bool useOutputDevice) + [Test] + public void DontTrackPitchValue_FromBeforePitchBend_ToAfterPitchBend() + { + var pitchBendTime = TimeSpan.FromMilliseconds(500); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(300); + var moveTo = TimeSpan.FromMilliseconds(700); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }, + setupPlayback: playback => playback.TrackPitchValue = false); + } + + [Retry(RetriesNumber)] + [Test] + public void DontTrackPitchValue_FromAfterPitchBend_ToAfterPitchBend() + { + var pitchBendTime = TimeSpan.FromMilliseconds(400); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(500); + var moveTo = TimeSpan.FromMilliseconds(700); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new StartEvent(), lastEventTime - (moveTo - moveFrom)), + }, + setupPlayback: playback => playback.TrackPitchValue = false); + } + + [Retry(RetriesNumber)] + [Test] + public void DontTrackPitchValue_FromAfterPitchBend_ToBeforePitchBend() + { + var pitchBendTime = TimeSpan.FromMilliseconds(500); + var lastEventTime = TimeSpan.FromSeconds(1); + var pitchValue = (ushort)234; + + var moveFrom = TimeSpan.FromMilliseconds(700); + var moveTo = TimeSpan.FromMilliseconds(300); + + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] + { + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) + }, + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime + moveFrom - moveTo), + new ReceivedEvent(new StartEvent(), lastEventTime + moveFrom - moveTo), + }, + setupPlayback: playback => playback.TrackPitchValue = false); + } + + [Retry(RetriesNumber)] + [Test] + public void DontTrackPitchValue_FromBeforePitchBend_ToPitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(1); var pitchValue = (ushort)234; var moveFrom = TimeSpan.FromMilliseconds(500); var moveTo = TimeSpan.FromMilliseconds(800); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] + { + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), + }, + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - eventsWillBeSent: new[] + expectedReceivedEvents: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), - new EventToSend(new StartEvent(), noteOffTime - (moveTo - moveFrom) - moveFrom) + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime + moveFrom - moveTo), }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + setupPlayback: playback => playback.TrackPitchValue = false); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_FromAfterPitchBend_ToPitchBend(bool useOutputDevice) + [Test] + public void DontTrackPitchValue_FromAfterPitchBend_ToPitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(1); var pitchValue = (ushort)234; - var moveFrom = TimeSpan.FromMilliseconds(1000); + var moveFrom = TimeSpan.FromMilliseconds(900); var moveTo = TimeSpan.FromMilliseconds(800); - CheckTrackPitchValue( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom - pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)) }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - pitchBendTime + moveFrom), + }, + setupPlayback: playback => playback.TrackPitchValue = false); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_EnableInMiddle_FromBeforePitchBend_ToAfterPitchBend(bool useOutputDevice) + [Test] + public void TrackPitchValue_DisableInMiddle_FromBeforePitchBend_ToAfterPitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); var programChangeTime = TimeSpan.FromSeconds(1); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(2); var pitchValue = (ushort)234; var programNumber = (SevenBitNumber)100; var moveFrom = TimeSpan.FromMilliseconds(500); var moveTo = TimeSpan.FromMilliseconds(1200); - var enableAfter = TimeSpan.FromMilliseconds(500); + var disableAfter = TimeSpan.FromMilliseconds(500); - CheckTrackPitchValueEnabledInMiddle( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new ProgramChangeEvent(programNumber), programChangeTime - pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - programChangeTime) + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)programChangeTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom + enableAfter), - new EventToSend(new StartEvent(), noteOffTime - (moveTo + enableAfter)) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)), + new PlaybackChangerBase(disableAfter, + p => p.TrackPitchValue = false), }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice, - enableAfter: enableAfter, - setupPlayback: playback => playback.TrackProgram = false); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - moveTo + moveFrom), + }, + setupPlayback: playback => + { + playback.TrackProgram = false; + }); } [Retry(RetriesNumber)] - [TestCase(true)] - [TestCase(false)] - public void TrackPitchValue_EnableInMiddle_FromAfterPitchBend_ToBeforePitchBend(bool useOutputDevice) + [Test] + public void TrackPitchValue_DisableInMiddle_FromAfterPitchBend_ToBeforePitchBend() { var pitchBendTime = TimeSpan.FromMilliseconds(800); - var noteOffTime = TimeSpan.FromSeconds(2); + var lastEventTime = TimeSpan.FromSeconds(2); var pitchValue = (ushort)234; var moveFrom = TimeSpan.FromMilliseconds(1000); var moveTo = TimeSpan.FromMilliseconds(500); - var enableAfter = TimeSpan.FromMilliseconds(150); + var disableAfter = TimeSpan.FromMilliseconds(150); - CheckTrackPitchValueEnabledInMiddle( - eventsToSend: new[] + CheckPlayback( + useOutputDevice: false, + initialPlaybackObjects: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new TimedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }) + .SetTime((MetricTimeSpan)pitchBendTime, TempoMap.Default), + new TimedEvent(new StartEvent()) + .SetTime((MetricTimeSpan)lastEventTime, TempoMap.Default), }, - eventsWillBeSent: new[] + actions: new[] { - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), - new EventToSend(new PitchBendEvent() { Channel = (FourBitNumber)4 }, moveFrom - pitchBendTime + enableAfter), - new EventToSend(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - (moveTo + enableAfter)), - new EventToSend(new StartEvent(), noteOffTime - pitchBendTime) + new PlaybackChangerBase(moveFrom, + p => p.MoveToTime((MetricTimeSpan)moveTo)), + new PlaybackChangerBase(disableAfter, + p => p.TrackPitchValue = false), }, - moveFrom: moveFrom, - moveTo: moveTo, - useOutputDevice: useOutputDevice, - enableAfter: enableAfter); + expectedReceivedEvents: new[] + { + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime), + new ReceivedEvent(new PitchBendEvent() { Channel = (FourBitNumber)4 }, moveFrom), + new ReceivedEvent(new PitchBendEvent(pitchValue) { Channel = (FourBitNumber)4 }, pitchBendTime - moveTo + moveFrom), + new ReceivedEvent(new StartEvent(), lastEventTime - moveTo + moveFrom), + }); } #endregion - - #region Private methods - - private void CheckTrackPitchValue( - ICollection eventsToSend, - ICollection eventsWillBeSent, - TimeSpan moveFrom, - TimeSpan moveTo, - bool useOutputDevice, - Action setupPlayback = null) => - CheckDataTracking( - p => - { - p.TrackPitchValue = true; - setupPlayback?.Invoke(p); - }, - eventsToSend, - eventsWillBeSent, - moveFrom, - moveTo, - useOutputDevice); - - private void CheckTrackPitchValueEnabledInMiddle( - ICollection eventsToSend, - ICollection eventsWillBeSent, - TimeSpan moveFrom, - TimeSpan moveTo, - bool useOutputDevice, - TimeSpan enableAfter, - Action setupPlayback = null) => - CheckDataTracking( - p => - { - p.TrackPitchValue = false; - setupPlayback?.Invoke(p); - }, - eventsToSend, - eventsWillBeSent, - moveFrom, - moveTo, - useOutputDevice, - enableAfter, - p => p.TrackPitchValue = true); - - #endregion } } diff --git a/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackProgram.cs b/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackProgram.cs index b84d771fb..ec141a6bd 100644 --- a/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackProgram.cs +++ b/DryWetMidi.Tests/Multimedia/Playback/PlaybackTests.TrackProgram.cs @@ -321,7 +321,11 @@ public void TrackProgram_EnableInMiddle_FromBeforeProgramChange_ToAfterProgramCh new ReceivedEvent(new ProgramChangeEvent(programNumber) { Channel = (FourBitNumber)4 }, moveFrom + enableAfter), new ReceivedEvent(new StartEvent(), moveFrom + lastEventTime - moveTo), }, - setupPlayback: playback => playback.TrackProgram = false); + setupPlayback: playback => + { + playback.TrackProgram = false; + playback.TrackPitchValue = false; + }); } [Retry(RetriesNumber)] @@ -676,7 +680,8 @@ public void TrackProgram_DisableInMiddle_FromBeforeProgramChange_ToAfterProgramC { new ReceivedEvent(new ProgramChangeEvent(programNumber) { Channel = (FourBitNumber)4 }, moveFrom), new ReceivedEvent(new StartEvent(), moveFrom + lastEventTime - moveTo), - }); + }, + setupPlayback: playback => playback.TrackPitchValue = false); } [Retry(RetriesNumber)] diff --git a/DryWetMidi/Multimedia/Playback/PlaybackDataTracker.cs b/DryWetMidi/Multimedia/Playback/PlaybackDataTracker.cs index 85a80710b..af59c6c01 100644 --- a/DryWetMidi/Multimedia/Playback/PlaybackDataTracker.cs +++ b/DryWetMidi/Multimedia/Playback/PlaybackDataTracker.cs @@ -171,7 +171,7 @@ public PlaybackDataTracker(TempoMap tempoMap) public bool TrackProgram { get; set; } = true; - public bool TrackPitchValue { get; set; } + public bool TrackPitchValue { get; set; } = true; public bool TrackControlValue { get; set; }