The automation engine is used to automate pieces of data, e.g. media position, scale, opacity, etc.
Always found automating parameters in the standard editors to be generally finicky. Ableton Live has a really good automation editor though, so I took a fair bit of inspiration from it:
- Each clip has its own keyframe/envelope editor that stretches the entirety of the clip. Effects also use this
- Tracks have the same, but it stretches the entire timeline.
- Automating project settings, or anything else really, will soon be do-able on a timeline automation specific track (allowing for more than just video/audio tracks)
The clip's automation sequence editor's target parameter can be changed by selecting a row (I call them "slots") in the property editor. The currently selected slot is what the clip shows (if clip automation is visible, click C to toggle)
I'm sure you know what key frame are; they just store a value at some time. I decided to store the time as a frame (relative to the project FPS) because it's much simpler, however, when you change the project FPS it will cause automation to run faster or slower depending on the frame rate difference, which is why I added a conversion popup when changing the FPS
In order for data to be automated, there needs to be an AutomationKey
. Automation keys are just a helper container to store the
key's full ID and data type, as well as storing all registered keys. Keys have a Domain
and Id
, which are joined by ::
to
form FullId
, which gets serialised and deserialises when saving and loading project, in order to identify a key from a
string (similar to WPF's dependency property system, except instead of an owner type it's a domain).
I'm not sure why I went with domain and not the owner type, but I don't see any reason to switch back and I don't plan on using reflection to update the values
I also sometimes call these automation parameters, similar to how VST has parameters
Sequences are basically just a list of key frames with a "default keyframe" too. The sequence is what calculates a final value by interpolating
between key frames at some specific input frame, via methods such as GetDoubleValue
, GetVector2Value
, etc.
The default key frame is used as backing storage for the default automation value of the automatable object, or put simply, it's the value when not using key frames.
This used to be called OverrideKeyFrame
but I renamed it to DefaultKeyFrame
because it mainly serves the above purpose, and not just storing the override value
The override mode is a way to temporarily disable automation. When there are no key frames present, the sequence is implicitly
in override mode (hence why I previously named that key frame OverrideKeyFrame
)
This is where automation keys and automation sequences are used. This class is used to map an AutomationKey
to an AutomationSequence
, and
also stores the automatable object that owns all of the sequences. Automation sequences are defined via the AutomationData
's AssignKey
, which
is where a new sequence is created and added to the internal dictionary. This is also where you provide your UpdateAutomationValueEventHandler
This event is used to tell an automatable object to query its backing value from its automation data. Automatable objects can have a field or property (or whatever they want) in order to store the value from the most recent automation update. #
This technically means, when in override mode, this event is just copying the value from the DefaultKeyFrame
to the object's field or
whatever (the event handler would call something like GetDoubleValue
which, in override mode, just accesses the default key frame's value)
However when there are key frames and the override mode is not enabled, the interpolated value is calculated during a call to something like GetDoubleValue
That newly updated value can then be used during rendering to do things. Rendering shouldn't reference the automation engine at all, and instead, should rely on that field, property, etc., being updated from this event