11namespace tracktion {
inline namespace engine
14AutomationRecordManager::AutomationRecordManager (Edit& ed)
15 : engine (ed.engine), edit (ed)
17 if (edit.shouldPlay())
23 else if (
auto t = juce::Thread::getCurrentThread())
26 jassert (juce::MessageManager::getInstance()->isThisTheMessageThread());
28 if (mml ==
nullptr || mml->lockWasGained())
29 edit.getTransport().addChangeListener (
this);
35 readingAutomation.referTo (edit.getTransport().state, IDs::automationRead,
nullptr,
true);
38AutomationRecordManager::~AutomationRecordManager()
40 if (edit.shouldPlay())
41 edit.getTransport().removeChangeListener (
this);
44void AutomationRecordManager::setReadingAutomation (
bool b)
46 if (readingAutomation != b)
48 readingAutomation = b;
51 engine.getExternalControllerManager().automationModeChanged (readingAutomation, writingAutomation);
55void AutomationRecordManager::setWritingAutomation (
bool b)
57 if (writingAutomation != b)
59 writingAutomation = b;
62 engine.getExternalControllerManager().automationModeChanged (readingAutomation, writingAutomation);
68 return TimeDuration::fromSeconds (
static_cast<double> (e.
getPropertyStorage().getProperty (SettingID::glideLength)));
76void AutomationRecordManager::changeListenerCallback (ChangeBroadcaster* source)
79 if (source == &edit.getTransport())
81 const bool isPlaying = edit.getTransport().isPlaying()
82 || edit.getTransport().isRecording();
84 if (wasPlaying != isPlaying)
86 wasPlaying = isPlaying;
91 jassert (recordedParams.isEmpty());
93 recordedParams.clear();
98bool AutomationRecordManager::isRecordingAutomation()
const
101 return recordedParams.size() > 0;
104bool AutomationRecordManager::isParameterRecording (AutomatableParameter* param)
const
108 for (
auto p : recordedParams)
109 if (&p->parameter == param)
115void AutomationRecordManager::punchOut (
bool toEnd)
119 if (recordedParams.size() > 0)
121 auto endTime = edit.getTransport().getPosition();
125 if (
auto epc = edit.getTransport().getCurrentPlaybackContext())
127 endTime = epc->isLooping() ?
std::max (epc->getUnloopedPosition(),
128 epc->getLoopTimes().getEnd())
129 : epc->getPosition();
132 for (
auto param : recordedParams)
135 endTime =
std::max (endTime, toPosition (param->parameter.getCurve().getLength()) + TimeDuration::fromSeconds (1.0));
137 applyChangesToParameter (param, endTime, toEnd);
138 param->parameter.resetRecordingStatus();
141 recordedParams.clear();
146void AutomationRecordManager::applyChangesToParameter (AutomationParamData* parameter, TimePosition end,
bool toEnd)
153 curve->setOwnerParameter (¶meter->parameter);
155 for (
int i = 0; i < parameter->changes.size(); ++i)
157 auto& change = parameter->changes.getReference (i);
159 if (i > 0 && change.time < parameter->changes.getReference (i - 1).time - TimeDuration::fromSeconds (0.1))
162 if (curve->getNumPoints() > 0)
164 newCurves.add (curve.release());
166 curve->setOwnerParameter (¶meter->parameter);
170 const float oldVal = (i == 0) ? (parameter->parameter.getCurve().getNumPoints() > 0 ? parameter->parameter.getCurve().getValueAt (change.time)
171 : parameter->originalValue)
172 : curve->getValueAt (change.
time);
174 const float newVal = parameter->parameter.snapToState (change.value);
176 if (parameter->parameter.isDiscrete())
178 curve->addPoint (change.time, oldVal, 0.0f);
179 curve->addPoint (change.time, newVal, 0.0f);
183 if (std::abs (oldVal - newVal) > (parameter->parameter.getValueRange().getLength() * 0.21f))
186 curve->addPoint (change.time - TimeDuration::fromSeconds (0.000001), oldVal, 0.0f);
188 curve->addPoint (change.time, oldVal, 0.0f);
191 curve->addPoint (change.time, newVal, 0.0f);
195 if (curve->getNumPoints() > 0)
196 newCurves.add (curve.release());
199 auto glideLength = getGlideSeconds (engine);
201 for (
int i = 0; i < newCurves.size(); ++i)
203 auto& curve = *newCurves.getUnchecked (i);
205 if (curve.getNumPoints() > 0)
207 auto startTime = curve.getPointTime (0);
210 if (i < newCurves.size() - 1)
211 endTime = curve.getPointTime (curve.getNumPoints() - 1);
215 if (parameter->parameter.getCurve().getNumPoints() == 0)
218 auto& c = parameter->parameter.getCurve();
219 TimeRange curveRange (startTime, endTime + glideLength);
220 c.mergeOtherCurve (curve, curveRange,
221 startTime, glideLength,
false, toEnd);
223 if (engine.getPropertyStorage().getProperty (SettingID::simplifyAfterRecording,
true))
224 c.simplify (curveRange.expanded (TimeDuration::fromSeconds (0.001)), 0.01, 0.002f);
229void AutomationRecordManager::toggleWriteAutomationMode()
233 setWritingAutomation (! isWritingAutomation());
236void AutomationRecordManager::postFirstAutomationChange (
AutomatableParameter& param,
float originalValue)
238 TRACKTION_ASSERT_MESSAGE_THREAD
245 recordedParams.add (entry.release());
250 TRACKTION_ASSERT_MESSAGE_THREAD
253 for (
auto p : recordedParams)
255 if (&p->parameter == ¶m)
257 p->changes.add (AutomationParamData::Change (time, value));
263void AutomationRecordManager::parameterBeingDeleted (AutomatableParameter& param)
267 for (
int i = recordedParams.size(); --i >= 0;)
268 if (&recordedParams.getUnchecked (i)->parameter == ¶m)
269 recordedParams.remove (i);
static ThreadPoolJob * getCurrentThreadPoolJob()
The Engine is the central class for all tracktion sessions.
PropertyStorage & getPropertyStorage() const
Returns the PropertyStorage user settings customisable XML file.
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
Represents a duration in real-life time.
constexpr double inSeconds() const
Returns the TimeDuration as a number of seconds.
Represents a position in real-life time.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.