Notes:
Names, Colors, Instrument – Extend to full Note List ; Dictionary
Grabbing
Repeat Button Note List
Double Hit
//===================================================================================== // // Purpose: Provide a mechanism for determining if a game world object is interactable // // This script should be attached to any object that needs touch, use or grab // // An optional highlight color can be set to change the object's appearance if it is // invoked. // //===================================================================================== namespace VRTK { using UnityEngine; using System.Collections.Generic; public struct InteractableObjectEventArgs { public GameObject interactingObject; } public delegate void InteractableObjectEventHandler(object sender, InteractableObjectEventArgs e); public class VRTK_InteractableObject : MonoBehaviour { public enum GrabAttachType { Fixed_Joint, Spring_Joint, Track_Object, Rotator_Track, Child_Of_Controller } public enum AllowedController { Both, Left_Only, Right_Only } public enum ControllerHideMode { Default, OverrideHide, OverrideDontHide, } [Header("Touch Interactions", order = 1)] public bool highlightOnTouch = false; public Color touchHighlightColor = Color.clear; public Vector2 rumbleOnTouch = Vector2.zero; public AllowedController allowedTouchControllers = AllowedController.Both; public ControllerHideMode hideControllerOnTouch = ControllerHideMode.Default; [Header("Grab Interactions", order = 2)] public bool isGrabbable = false; public bool isDroppable = true; public bool isSwappable = true; public bool holdButtonToGrab = true; public Vector2 rumbleOnGrab = Vector2.zero; public AllowedController allowedGrabControllers = AllowedController.Both; public bool precisionSnap; public Transform rightSnapHandle; public Transform leftSnapHandle; public ControllerHideMode hideControllerOnGrab = ControllerHideMode.Default; [Header("Grab Mechanics", order = 3)] public GrabAttachType grabAttachMechanic = GrabAttachType.Fixed_Joint; public float detachThreshold = 500f; public float springJointStrength = 500f; public float springJointDamper = 50f; public float throwMultiplier = 1f; public float onGrabCollisionDelay = 0f; [Header("Use Interactions", order = 4)] public bool isUsable = false; public bool useOnlyIfGrabbed = false; public bool holdButtonToUse = true; public bool pointerActivatesUseAction = false; public Vector2 rumbleOnUse = Vector2.zero; public AllowedController allowedUseControllers = AllowedController.Both; public ControllerHideMode hideControllerOnUse = ControllerHideMode.Default; public event InteractableObjectEventHandler InteractableObjectTouched; public event InteractableObjectEventHandler InteractableObjectUntouched; public event InteractableObjectEventHandler InteractableObjectGrabbed; public event InteractableObjectEventHandler InteractableObjectUngrabbed; public event InteractableObjectEventHandler InteractableObjectUsed; public event InteractableObjectEventHandler InteractableObjectUnused; protected Rigidbody rb; protected GameObject touchingObject = null; protected GameObject grabbingObject = null; protected GameObject usingObject = null; private int usingState = 0; private Dictionary<string, Color> originalObjectColours; private Transform grabbedSnapHandle; private Transform trackPoint; private bool customTrackPoint = false; private Transform originalControllerAttachPoint; private Transform previousParent; private bool previousKinematicState; private bool previousIsGrabbable; private bool forcedDropped; public bool CheckHideMode(bool defaultMode, ControllerHideMode overrideMode) { switch (overrideMode) { case VRTK_InteractableObject.ControllerHideMode.OverrideDontHide: return false; case VRTK_InteractableObject.ControllerHideMode.OverrideHide: return true; } // default: do not change return defaultMode; } public virtual void OnInteractableObjectTouched(InteractableObjectEventArgs e) { if (InteractableObjectTouched != null) { InteractableObjectTouched(this, e); } } public virtual void OnInteractableObjectUntouched(InteractableObjectEventArgs e) { if (InteractableObjectUntouched != null) { InteractableObjectUntouched(this, e); } } public virtual void OnInteractableObjectGrabbed(InteractableObjectEventArgs e) { if (InteractableObjectGrabbed != null) { InteractableObjectGrabbed(this, e); } } public virtual void OnInteractableObjectUngrabbed(InteractableObjectEventArgs e) { if (InteractableObjectUngrabbed != null) { InteractableObjectUngrabbed(this, e); } } public virtual void OnInteractableObjectUsed(InteractableObjectEventArgs e) { if (InteractableObjectUsed != null) { InteractableObjectUsed(this, e); } } public virtual void OnInteractableObjectUnused(InteractableObjectEventArgs e) { if (InteractableObjectUnused != null) { InteractableObjectUnused(this, e); } } public InteractableObjectEventArgs SetInteractableObjectEvent(GameObject interactingObject) { InteractableObjectEventArgs e; e.interactingObject = interactingObject; return e; } public bool IsTouched() { return (touchingObject != null); } public bool IsGrabbed() { return (grabbingObject != null); } public bool IsUsing() { return (usingObject != null); } public virtual void StartTouching(GameObject currentTouchingObject) { OnInteractableObjectTouched(SetInteractableObjectEvent(currentTouchingObject)); touchingObject = currentTouchingObject; } public virtual void StopTouching(GameObject previousTouchingObject) { OnInteractableObjectUntouched(SetInteractableObjectEvent(previousTouchingObject)); touchingObject = null; StopUsingOnControllerChange(previousTouchingObject); } public virtual void Grabbed(GameObject currentGrabbingObject) { OnInteractableObjectGrabbed(SetInteractableObjectEvent(currentGrabbingObject)); ForceReleaseGrab(); RemoveTrackPoint(); grabbingObject = currentGrabbingObject; SetTrackPoint(grabbingObject); if (!isSwappable) { previousIsGrabbable = isGrabbable; isGrabbable = false; } } public virtual void Ungrabbed(GameObject previousGrabbingObject) { OnInteractableObjectUngrabbed(SetInteractableObjectEvent(previousGrabbingObject)); RemoveTrackPoint(); grabbedSnapHandle = null; grabbingObject = null; LoadPreviousState(); StopUsingOnControllerChange(previousGrabbingObject); } private void StopUsingOnControllerChange(GameObject previousController) { var usingObject = previousController.GetComponent<VRTK_InteractUse>(); if (usingObject) { if (holdButtonToUse) { usingObject.ForceStopUsing(); } else { usingObject.ForceResetUsing(); } } } public virtual void StartUsing(GameObject currentUsingObject) { OnInteractableObjectUsed(SetInteractableObjectEvent(currentUsingObject)); usingObject = currentUsingObject; } public virtual void StopUsing(GameObject previousUsingObject) { OnInteractableObjectUnused(SetInteractableObjectEvent(previousUsingObject)); usingObject = null; } public virtual void ToggleHighlight(bool toggle) { ToggleHighlight(toggle, Color.clear); } public virtual void ToggleHighlight(bool toggle, Color globalHighlightColor) { if (highlightOnTouch) { if (toggle && !IsGrabbed() && !IsUsing()) { Color color = (touchHighlightColor != Color.clear ? touchHighlightColor : globalHighlightColor); if (color != Color.clear) { var colorArray = BuildHighlightColorArray(color); ChangeColor(colorArray); } } else { if (originalObjectColours == null) { Debug.LogError("VRTK_InteractableObject has not had the Start() method called, if you are inheriting this class then call base.Start() in your Start() method."); return; } ChangeColor(originalObjectColours); } } } public int UsingState { get { return usingState; } set { usingState = value; } } public void PauseCollisions() { if (onGrabCollisionDelay > 0f) { if (GetComponent<Rigidbody>()) { GetComponent<Rigidbody>().detectCollisions = false; } foreach (Rigidbody rb in GetComponentsInChildren<Rigidbody>()) { rb.detectCollisions = false; } Invoke("UnpauseCollisions", onGrabCollisionDelay); } } public bool AttachIsTrackObject() { return (grabAttachMechanic == GrabAttachType.Track_Object || grabAttachMechanic == GrabAttachType.Rotator_Track); } public void ZeroVelocity() { if (GetComponent<Rigidbody>()) { GetComponent<Rigidbody>().velocity = Vector3.zero; GetComponent<Rigidbody>().angularVelocity = Vector3.zero; } } public void SaveCurrentState() { if (grabbingObject == null) { previousParent = transform.parent; previousKinematicState = rb.isKinematic; } } public void ToggleKinematic(bool state) { rb.isKinematic = state; } public GameObject GetGrabbingObject() { return grabbingObject; } public bool IsValidInteractableController(GameObject actualController, AllowedController controllerCheck) { if (controllerCheck == AllowedController.Both) { return true; } var controllerHand = VRTK_DeviceFinder.GetControllerHandType(controllerCheck.ToString().Replace("_Only", "")); return (VRTK_DeviceFinder.IsControllerOfHand(actualController, controllerHand)); } public void ForceStopInteracting() { if (touchingObject != null && touchingObject.activeInHierarchy) { touchingObject.GetComponent<VRTK_InteractTouch>().ForceStopTouching(); forcedDropped = true; } if (grabbingObject != null && grabbingObject.activeInHierarchy) { grabbingObject.GetComponent<VRTK_InteractTouch>().ForceStopTouching(); grabbingObject.GetComponent<VRTK_InteractGrab>().ForceRelease(); forcedDropped = true; } if (usingObject != null && usingObject.activeInHierarchy) { usingObject.GetComponent<VRTK_InteractTouch>().ForceStopTouching(); usingObject.GetComponent<VRTK_InteractUse>().ForceStopUsing(); forcedDropped = true; } } public void SetGrabbedSnapHandle(Transform handle) { grabbedSnapHandle = handle; } public void RegisterTeleporters() { foreach (var teleporter in FindObjectsOfType<VRTK_BasicTeleport>()) { teleporter.Teleported += new TeleportEventHandler(OnTeleported); } } protected virtual void Awake() { rb = GetComponent<Rigidbody>(); // If there is no rigid body, add one and set it to 'kinematic'. if (!rb) { rb = gameObject.AddComponent<Rigidbody>(); rb.isKinematic = true; } rb.maxAngularVelocity = float.MaxValue; forcedDropped = false; } protected virtual void Start() { originalObjectColours = StoreOriginalColors(); RegisterTeleporters(); } protected virtual void Update() { if (!gameObject.activeInHierarchy) { ForceStopInteracting(); } if (AttachIsTrackObject()) { CheckBreakDistance(); } } protected virtual void FixedUpdate() { if (trackPoint) { switch (grabAttachMechanic) { case GrabAttachType.Rotator_Track: FixedUpdateRotatorTrack(); break; case GrabAttachType.Track_Object: FixedUpdateTrackObject(); break; } } } protected virtual void OnDisable() { ForceStopInteracting(); } protected virtual void OnEnable() { if(forcedDropped) { LoadPreviousState(); } } protected virtual void OnJointBreak(float force) { ForceReleaseGrab(); } protected virtual void LoadPreviousState() { if (gameObject.activeInHierarchy) { transform.parent = previousParent; forcedDropped = false; } rb.isKinematic = previousKinematicState; if (!isSwappable) { isGrabbable = previousIsGrabbable; } } private void ForceReleaseGrab() { if (grabbingObject) { grabbingObject.GetComponent<VRTK_InteractGrab>().ForceRelease(); } } private void UnpauseCollisions() { if (GetComponent<Rigidbody>()) { GetComponent<Rigidbody>().detectCollisions = true; } foreach (Rigidbody rb in GetComponentsInChildren<Rigidbody>()) { rb.detectCollisions = true; } } private Renderer[] GetRendererArray() { return (GetComponents<Renderer>().Length > 0 ? GetComponents<Renderer>() : GetComponentsInChildren<Renderer>()); } private Dictionary<string, Color> StoreOriginalColors() { Dictionary<string, Color> colors = new Dictionary<string, Color>(); foreach (Renderer renderer in GetRendererArray()) { if (renderer.material.HasProperty("_Color")) { colors[renderer.gameObject.name] = renderer.material.color; } } return colors; } private Dictionary<string, Color> BuildHighlightColorArray(Color color) { Dictionary<string, Color> colors = new Dictionary<string, Color>(); foreach (Renderer renderer in GetRendererArray()) { if (renderer.material.HasProperty("_Color")) { colors[renderer.gameObject.name] = color; } } return colors; } private void ChangeColor(Dictionary<string, Color> colors) { foreach (Renderer renderer in GetRendererArray()) { if (renderer.material.HasProperty("_Color") && colors.ContainsKey(renderer.gameObject.name)) { renderer.material.color = colors[renderer.gameObject.name]; } } } private void CheckBreakDistance() { if (trackPoint) { float distance = Vector3.Distance(trackPoint.position, transform.position); if (distance > (detachThreshold / 1000)) { ForceReleaseGrab(); } } } private void SetTrackPoint(GameObject point) { var controllerPoint = point.transform; var grabScript = point.GetComponent<VRTK_InteractGrab>(); if (grabScript && grabScript.controllerAttachPoint) { controllerPoint = grabScript.controllerAttachPoint.transform; } if (AttachIsTrackObject() && precisionSnap) { trackPoint = new GameObject(string.Format("[{0}]TrackObject_PrecisionSnap_AttachPoint", gameObject.name)).transform; trackPoint.parent = point.transform; customTrackPoint = true; if (grabAttachMechanic == GrabAttachType.Track_Object) { trackPoint.position = transform.position; trackPoint.rotation = transform.rotation; } else { trackPoint.position = controllerPoint.position; trackPoint.rotation = controllerPoint.rotation; } } else { trackPoint = controllerPoint; customTrackPoint = false; } originalControllerAttachPoint = new GameObject(string.Format("[{0}]Original_Controller_AttachPoint", grabbingObject.name)).transform; originalControllerAttachPoint.parent = transform; originalControllerAttachPoint.position = trackPoint.position; originalControllerAttachPoint.rotation = trackPoint.rotation; } private void RemoveTrackPoint() { if (customTrackPoint && trackPoint) { Destroy(trackPoint.gameObject); } else { trackPoint = null; } if (originalControllerAttachPoint) { Destroy(originalControllerAttachPoint.gameObject); } } private void FixedUpdateRotatorTrack() { var rotateForce = trackPoint.position - originalControllerAttachPoint.position; rb.AddForceAtPosition(rotateForce, originalControllerAttachPoint.position, ForceMode.VelocityChange); } private void FixedUpdateTrackObject() { float maxDistanceDelta = 10f; Quaternion rotationDelta; Vector3 positionDelta; float angle; Vector3 axis; if (grabbedSnapHandle != null) { rotationDelta = trackPoint.rotation * Quaternion.Inverse(grabbedSnapHandle.rotation); positionDelta = trackPoint.position - grabbedSnapHandle.position; } else { rotationDelta = trackPoint.rotation * Quaternion.Inverse(transform.rotation); positionDelta = trackPoint.position - transform.position; } rotationDelta.ToAngleAxis(out angle, out axis); angle = (angle > 180 ? angle -= 360 : angle); if (angle != 0) { Vector3 angularTarget = angle * axis; rb.angularVelocity = Vector3.MoveTowards(rb.angularVelocity, angularTarget, maxDistanceDelta); } Vector3 velocityTarget = positionDelta / Time.fixedDeltaTime; rb.velocity = Vector3.MoveTowards(rb.velocity, velocityTarget, maxDistanceDelta); } private void OnTeleported(object sender, DestinationMarkerEventArgs e) { if (AttachIsTrackObject() && trackPoint) { transform.position = grabbingObject.transform.position; } } } }
https://github.com/TomorrowTodayLabs/NewtonVR/
using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; using System; using VRTK; public class Argos_Note { public int nIdx; public double startTime; public float phaseBase; public double phase; public double attack; public double decay; public double amplitude; public bool isPlaying = false; public Argos_Note(int nI, double sT, float pB, double aT, double dC, double amp) { nIdx = nI; startTime = sT; phaseBase = pB; attack = aT; decay = dC; amplitude = amp; phase = 0; } } [RequireComponent(typeof(AudioSource))] public class Argos_Sound_Gen : MonoBehaviour { AudioSource audioSource; public List<Argos_Note> noteList = new List<Argos_Note>(); private int noteCount = 0; public Argos_Note[] voiceArray = new Argos_Note[10]; public Text dbg_Text; public float estimatedLatency { get; protected set; } public int globalSampleRate; [Range(0, 5)] public double gain = 1; [Range(0.01f, 0.02f)] public double phase1base = 0.01; [Range(0.0f, 1f)] public double attack = 0.01; [Range(0.0f, 5f)] public double decay = 0.01; public VRTK_Control attack_Slider; public VRTK_Control decay_Slider; public VRTK_Control echo_Delay_Slider; public VRTK_Control echo_Decay_Slider; public VRTK_Control echo_WetMix_Slider; public VRTK_Control echo_DryMix_Slider; private double amp = 0.0F; private double phase = 0.0F; private int accent; private bool running = false; private bool seqOn = false; int noteIDX = 0; private AudioEchoFilter aEchoFilter; //private float[] _dtime = { 0.004124562f, 0.00389302f, 0.003674579f, 0.003468248f, 0.003273644f, 0.003089948f, 0.002916472f, // 0.002752773f, 0.00259828f, 0.002452483f, 0.002314815f, 0.002184885f, 0.002062281f}; private float[] _dtime = { 0.015571473f, //C2 0 0.014697237f, //Db2 1 0.013873474f, //D2 2 0.013094147f, //Eb2 3 #region Note_Periods 0.012359412f, //E2 4 0.011665889f, //F2 5 0.011010791f, //F#2 6 0.01039285f, //G2 7 0.009809692f, //Ab2 8 0.009259259f, //A2 9 0.008739731f, //Bb2 10 0.008248783f, //B2 11 0.007786343f, //C3 12 0.007349159f, //Db3 13 0.006936737f, //D3 14 0.006547073f, //Eb3 15 0.006179706f, //E3 16 0.005832944f, //F3 17 0.005505698f, //F#3 18 0.005196695f, //G3 19 0.004904846f, //Ab3 20 0.00462963f, //A3 21 0.004369865f, //Bb3 22 0.004124562f, //B3 23 0.00389302f, //C4 24 0.003674579f, //Db4 25 0.003468248f, //D4 26 0.003273644f, //Eb4 27 0.003089948f, //E4 28 0.002916472f, //F4 29 0.002752773f, //F#4 30 0.00259828f, //G4 31 0.002452483f, //Ab4 32 0.002314815f, //A4 33 --- A 432 0.002184885f, //Bb4 34 0.002062281f, //B4 35 0.00194651f, //C4 36 0.001837256f, 0.001734154f, 0.001636822f, 0.00154495f, 0.001458236f, 0.001376406f, 0.00129914f, 0.001226227f, 0.001157407f, 0.001092442f, 0.00103113f, 0.000973264f, 0.000918636f, 0.000867077f, 0.000818411f, 0.000772475f, 0.000729123f, 0.000688198f, 0.000649574f, 0.000613117f, 0.000578704f, 0.000546224f, 0.000515568f, 0.00048663f, 0.000459318f, 0.000433539f, 0.000409205f, 0.000386239f, 0.00036456f, 0.000344099f, 0.000324786f, 0.000306558f, 0.000289352f, 0.000273112f, 0.000257783f, 0.000243315f, 0.000229659f, 0.000216769f, 0.000204603f, 0.000193119f, 0.00018228f, 0.00017205f, 0.000162393f, 0.000153279f, 0.000144676f, 0.000136556f, 0.000128892f }; #endregion void Awake() { // Create an audio source. audioSource = gameObject.GetComponent<AudioSource>(); audioSource.playOnAwake = false; audioSource.loop = true; } void Start() { globalSampleRate = AudioSettings.outputSampleRate; for(int i = 0; i<10; i++) { voiceArray[i] = null; } if(attack_Slider) { attack_Slider.defaultEvents.OnValueChanged.AddListener(AttackSlider_Change); } if (decay_Slider) { decay_Slider.defaultEvents.OnValueChanged.AddListener(DecaySlider_Change); } aEchoFilter = GetComponent<AudioEchoFilter>(); if (echo_Delay_Slider) { echo_Delay_Slider.defaultEvents.OnValueChanged.AddListener(Echo_Delay_Slider_Change); } if (echo_Decay_Slider) { echo_Decay_Slider.defaultEvents.OnValueChanged.AddListener(Echo_Decay_Slider_Change); } if (echo_WetMix_Slider) { echo_WetMix_Slider.defaultEvents.OnValueChanged.AddListener(Echo_WetMix_Slider_Change); } if (echo_DryMix_Slider) { echo_WetMix_Slider.defaultEvents.OnValueChanged.AddListener(Echo_DryMix_Slider_Change); } } private void AttackSlider_Change(float value) { attack = value; } private void DecaySlider_Change(float value) { decay = value; } private void Echo_Delay_Slider_Change(float value) { aEchoFilter.delay = value; } private void Echo_Decay_Slider_Change(float value) { aEchoFilter.decayRatio = value; } private void Echo_WetMix_Slider_Change(float value) { aEchoFilter.wetMix = value; } private void Echo_DryMix_Slider_Change(float value) { aEchoFilter.dryMix = value; } void OnApplicationPause(bool paused) { } float accum = 0.0f; void Update() { //if (seqOn) test //{ // accum += Time.deltaTime; // if(accum > 1.7f) // { // playNote(noteIDX, 0.8f); // if (++noteIDX > _dtime.Length - 1) // { // seqOn = false; // noteIDX = 0; // } // accum = 0f; // } //} } public void playNote(int idx, float amp) { Argos_Note aN = new Argos_Note(idx, (double)AudioSettings.dspTime, _dtime[idx], attack, decay, amp); aN.isPlaying = true; if (noteList.Count < 500) { noteList.Add(aN); } StartNewNote(aN); } public void StartNewNote(Argos_Note aN) { for(int i = 0; i<10; i++) { if(voiceArray[i] == null || !voiceArray[i].isPlaying) { voiceArray[i] = aN; return; } } //else replace oldest double minStartTime = double.MaxValue; int repIdx = 0; for(int j = 0; j < 10; j++) { if(voiceArray[j].startTime < minStartTime) { minStartTime = voiceArray[j].startTime; repIdx = j; } } voiceArray[repIdx] = aN; } double EnvelopShaper(Argos_Note aN, double sampTime) { amp = 0; if (sampTime - aN.startTime < aN.attack) { amp = CubicEaseIn(sampTime - aN.startTime, 0, 1, aN.attack); } else if (accumTime - aN.startTime < aN.attack + aN.decay) { amp = CubicEaseOut((sampTime - aN.startTime - aN.attack), 1, -1, aN.decay); } else { amp = 0; aN.isPlaying = false; } return amp; } float dTIME = 0f; double dt; int testCount = 0; double accumTime; void OnAudioFilterRead(float[] data, int channels) { //TESTER ACTIVE VOICE COUNT if (++testCount > 10) { int active = 0; for(int po = 0; po<10; po++) { if (voiceArray[po] != null) { if (voiceArray[po].isPlaying) active++; } } dbg_Text.text = "active Voices = " + active.ToString(); testCount = 0; } accumTime = (double)AudioSettings.dspTime; double samples = AudioSettings.dspTime * globalSampleRate; int dataLen = data.Length / channels; double deltTime = (AudioSettings.dspTime / samples);//HIGHLY REDUNDANT - FIX int n = 0; double a; double sampTime = accumTime; //dt = deltTime / dTIME * 2f * Mathf.PI; while (n < dataLen) { sampTime += deltTime; //do 10 voices a = 0; for (int v = 0; v<10; v++) { if (voiceArray[v] != null) { if (voiceArray[v].isPlaying) { a += voiceArray[v].amplitude * EnvelopShaper(voiceArray[v], sampTime) * Math.Sin(voiceArray[v].phase); } } } float x = (float)(a); int i = 0; while (i < channels) { data[n * channels + i] = x; i++; } for (int u = 0; u < 10; u++) { if (voiceArray[u] != null) { if (voiceArray[u].isPlaying) { dt = deltTime / voiceArray[u].phaseBase * 2f * Mathf.PI; voiceArray[u].phase += dt; if (voiceArray[u].phase > Math.PI * 2f) voiceArray[u].phase = 0f; } } } n++; } } /// </summary> /// <param name= t "current">how long into the ease are we</param> /// <param name= b "initialValue">starting value if current were 0</param> /// <param name= c "totalChange">total change in the value (not the end value, but the end - start)</param> /// <param name= d "duration">the total amount of time (when current == duration, the returned value will == initial + totalChange)</param> /// <returns></returns> public static double CubicEaseOut(double t, double b, double c, double d) { if (t < d) { return c * ((t = t / d - 1) * t * t + 1) + b; } else { return b + c; } } public static double CubicEaseIn(double t, double b, double c, double d) { if (t < d) { return c * (t /= d) * t * t + b; } else { return b + c; } } }
Three Instruments – Sliding Helices
To record Media – Track Both Hands – Two Channels MIDI – Start With Arms in View – Poss Astral Proj
Capture Artist Moves and And Midi Events are Placed at the location of the associated Controller.
To play back in time – The movement must follow what was recorded in Time.