11namespace tracktion {
inline namespace engine
16namespace EditPlaybackContextInternal
18 inline int& getThreadPoolStrategyType()
20 static int type =
static_cast<int> (tracktion::graph::ThreadPoolStrategy::lightweightSemHybrid);
24 inline bool& getPooledMemoryFlag()
26 static bool usePool =
false;
30 inline bool& getNodeMemorySharingFlag()
32 static bool useSharing =
false;
36 inline bool& getAudioWorkgroupFlag()
38 static bool useAudioWorkgroup =
false;
39 return useAudioWorkgroup;
44 if (! getAudioWorkgroupFlag())
47 return e.getDeviceManager().deviceManager.getDeviceAudioWorkgroup();
50 inline size_t getMaxNumThreadsToUse (Edit& edit)
52 if (edit.getIsPreviewEdit())
55 auto wg = getAudioWorkgroupIfEnabled (edit.engine);
56 return wg ? wg.getMaxParallelThreadCount() - 1
90 const auto sourceTimelineTime = TimePosition::fromSamples (sourcePlayHead.
getPosition(), sampleRate);
91 const auto millisecondsSinceEpoch = std::chrono::duration_cast<std::chrono::milliseconds> (sourcePlayHead.
getLastUserInteractionTime().time_since_epoch()).count();
92 const juce::Time sourceLastInteractionTime (
static_cast<int64_t> (millisecondsSinceEpoch));
94 const auto destTimelineTime = TimePosition::fromSamples (destPlayHead.
getPosition(), sampleRate);
95 const auto destLoopDuration = TimeDuration::fromSamples (destPlayHead.
getLoopRange().
getLength(), sampleRate);
97 return getSyncAction (sourceTimelineTime, sourcePlayHead.
isPlaying(), sourceLastInteractionTime,
98 destTimelineTime, destPlayHead.
isLooping(), destLoopDuration);
104 previousBarTime = previousBarTime_;
105 syncInterval = syncInterval_;
111 TimePosition previousBarTime;
112 TimeDuration syncInterval;
113 TimePosition lastSourceTimelineTime;
114 bool hasSynced =
false, isValid =
false;
116 SyncAndPosition getSyncAction (
const TimePosition sourceTimelineTime,
const bool sourceIsPlaying,
const juce::Time sourceLastInteractionTime,
117 const TimePosition destTimelineTime,
const bool destIsLooping,
const TimeDuration destLoopDuration)
124 const auto sourceDurationSinceLastBarStart = TimeDuration::fromSeconds (
std::fmod ((sourceTimelineTime - previousBarTime).inSeconds(), syncInterval.inSeconds()));
125 jassert (sourceTimelineTime - previousBarTime >= TimeDuration());
126 jassert (sourceDurationSinceLastBarStart > TimeDuration());
128 auto newTimelineTime = sourceDurationSinceLastBarStart - syncInterval;
131 if (destIsLooping && newTimelineTime < -syncInterval)
132 newTimelineTime = sourceDurationSinceLastBarStart;
134 newTimelineTime = TimeDuration::fromSeconds (
std::fmod (newTimelineTime.inSeconds(), destLoopDuration.inSeconds()));
140 if (! sourceIsPlaying || std::abs (lastSourceTimelineTime.inSeconds() - sourceTimelineTime.inSeconds()) > 0.2)
144 if (! sourceIsPlaying || rt.inSeconds() < 0.2)
154 const auto sourceDurationSinceLastBarStart = TimeDuration::fromSeconds (
std::fmod ((sourceTimelineTime - previousBarTime).inSeconds(), syncInterval.inSeconds()));
155 auto newTimelineTime = syncInterval *
std::floor ((destTimelineTime.inSeconds() + 0.1) / syncInterval.inSeconds()) + sourceDurationSinceLastBarStart;
156 newTimelineTime = TimeDuration::fromSeconds (
std::fmod (newTimelineTime.inSeconds(), destLoopDuration.inSeconds()));
172 : editPlaybackContext (epc),
173 player (processState,
175 EditPlaybackContextInternal::getAudioWorkgroupIfEnabled (tempoSequence.edit.engine)),
176 maxNumThreads (maxNumThreadsToUse)
181 const auto editTime = syncRange.start.
time;
182 editPlaybackContext.edit.
updateModifierTimers (editTime,
static_cast<int> (getNumSamples (syncRange)));
183 editPlaybackContext.midiDispatcher.masterTimeUpdate (editTime);
185 #if TRACKTION_ENABLE_ABLETON_LINK
190 setNumThreads (numThreads);
191 player.enablePooledMemoryAllocations (EditPlaybackContextInternal::getPooledMemoryFlag());
192 player.enableNodeMemorySharing (EditPlaybackContextInternal::getNodeMemorySharingFlag());
195 void setNumThreads (
size_t numThreads)
206 player.setNode (std::move (node), sampleRate, blockSize);
208 if (
auto currentNode = player.getNode())
209 latencySamples = currentNode->getNodeProperties().latencyNumSamples;
217 int getLatencySamples()
const
219 return latencySamples;
237 positionUpdatePending =
true;
248 void postRollInToLoop (
double newPosition)
252 positionUpdatePending =
true;
255 void setSpeedCompensation (
double plusOrMinus)
257 speedCompensation =
juce::jlimit (-10.0, 10.0, plusOrMinus);
260 void setTempoAdjustment (
double plusOrMinusProportion)
262 blockLengthScaleFactor = 1.0 +
std::clamp (plusOrMinusProportion, -0.5, 0.5);
265 void checkForTempoSequenceChanges()
269 if (internalSequence.hash() == tempoState.hash)
272 const auto lastPositionRemapped = internalSequence.toTime (tempoState.lastBeatPosition);
273 const auto lastSampleRemapped = toSamples (lastPositionRemapped, getSampleRate());
277 void updateReferenceSampleRange (
int numSamples)
279 if (speedCompensation != 0.0)
280 numSamples =
juce::roundToInt (numSamples * (1.0 + (speedCompensation * 0.01)));
282 double sampleDuration =
static_cast<double> (numSamples);
284 if (blockLengthScaleFactor != 1.0)
285 sampleDuration *= blockLengthScaleFactor;
289 numSamplesToProcess =
static_cast<choc::buffer::FrameCount
> (numSamples);
293 checkForTempoSequenceChanges();
298 const double sampleRate = getSampleRate();
299 const auto currentPos = tracktion::graph::sampleToTime (playHead.
getPosition(), sampleRate);
301 static_cast<double> (newReferenceSampleRange.
getEnd()));
303 playHead.
setPosition (tracktion::graph::timeToSample (currentPos, sampleRate));
306 void process (
float*
const* allChannels,
int numChannels,
int destNumSamples)
308 const auto referenceSampleRange = getReferenceSampleRange();
309 const double sampleRate = getSampleRate();
313 bool shouldPerformPositionChange =
true;
315 if (pendingPositionJumpTimeValid)
321 const bool loopEndIsInThisBlock = playHead.
isLooping()
324 if (loopEndIsInThisBlock)
326 pendingPositionJumpTimeValid =
false;
327 pendingPositionJumpTime = 0.0;
329 pendingPosition = 0.0;
330 positionUpdatePending =
false;
332 shouldPerformPositionChange =
false;
335 if (currentTimeSeconds.contains (jumpTimeSeconds))
337 pendingPositionJumpTimeValid =
false;
338 pendingPositionJumpTime = 0.0;
342 shouldPerformPositionChange =
false;
346 if (shouldPerformPositionChange)
348 if (positionUpdatePending.
exchange (
false))
360 scratchMidiBuffer.clear();
362 if (isUsingInterpolator || destNumSamples != (
int) numSamplesToProcess)
365 isUsingInterpolator =
true;
366 ensureNumInterpolators (numChannels);
369 scratchAudioBuffer.
setSize (numChannels, (
int) numSamplesToProcess,
false,
false,
true);
370 scratchAudioBuffer.
clear();
376 const double ratio = numSamplesToProcess / (
double) destNumSamples;
378 for (
int channel = 0; channel < numChannels; ++channel)
381 const auto dest = allChannels[channel];
383 interpolators[(
size_t) channel]->processAdding (ratio, src, dest, destNumSamples, 1.0f);
388 auto audioView = choc::buffer::createChannelArrayView (allChannels,
389 (choc::buffer::ChannelCount) numChannels,
390 numSamplesToProcess);
396 processState.editBeatRange.getEnd() };
399 double getSampleRate()
const
413 ProcessState processState { playHeadState, tempoSequence };
419 const size_t maxNumThreads;
421 int latencySamples = 0;
422 choc::buffer::FrameCount numSamplesToProcess = 0;
425 std::atomic<bool> positionUpdatePending {
false }, pendingRollInToLoop {
false }, pendingPositionJumpTimeValid {
false };
426 double speedCompensation = 0.0, blockLengthScaleFactor = 1.0;
428 bool isUsingInterpolator =
false;
436 TempoState tempoState;
444 void ensureNumInterpolators (
int numRequired)
446 for (
size_t i = interpolators.
size(); i < (
size_t) numRequired; ++i)
452EditPlaybackContext::ScopedDeviceListReleaser::ScopedDeviceListReleaser (
EditPlaybackContext& e,
bool reallocate)
453 : owner (e), shouldReallocate (reallocate)
457 owner.isAllocated =
false;
459 owner.releaseDeviceList();
462EditPlaybackContext::ScopedDeviceListReleaser::~ScopedDeviceListReleaser()
464 if (shouldReallocate)
465 owner.rebuildDeviceList();
470 : edit (tc.edit), transport (tc)
472 if (edit.isRendering())
475 TRACKTION_LOG_ERROR(
"EditPlaybackContext created whilst rendering");
478 if (edit.shouldPlay())
481 edit.engine.getEngineBehaviour().getNumberOfCPUsToUseForAudio(),
482 EditPlaybackContextInternal::getMaxNumThreadsToUse (edit));
486 edit.engine.getDeviceManager().addContext (
this);
489 nodePlaybackContext->playHead.setPosition (toSamples (transport.getPosition(),
490 edit.engine.getDeviceManager().getSampleRate()));
496EditPlaybackContext::~EditPlaybackContext()
498 TRACKTION_ASSERT_MESSAGE_THREAD
500 edit.engine.getDeviceManager().removeContext (
this);
503void EditPlaybackContext::releaseDeviceList()
505 TRACKTION_ASSERT_MESSAGE_THREAD
519void EditPlaybackContext::rebuildDeviceList()
521 TRACKTION_ASSERT_MESSAGE_THREAD
524 jassert (waveInputs.isEmpty() && midiInputs.isEmpty()
525 && midiOutputs.isEmpty() && waveOutputs.isEmpty());
527 auto& dm = edit.engine.getDeviceManager();
529 for (
auto mo : dm.midiOutputs)
531 midiOutputs.add (mo->createInstance (*this));
533 for (
auto mi : dm.midiInputs)
534 if (mi->isEnabled() && mi->isAvailableToEdit())
535 midiInputs.add (mi->createInstance (*this));
537 for (
auto wo : dm.waveOutputs)
539 waveOutputs.add (wo->createInstance (*this));
541 for (
auto wi : dm.waveInputs)
542 if (wi->isEnabled() && wi->isAvailableToEdit())
543 waveInputs.add (wi->createInstance (*this));
547 auto& twid = at->getWaveInputDevice();
548 auto& tmid = at->getMidiInputDevice();
550 if (twid.isEnabled()) waveInputs.add (twid.createInstance (*
this));
551 if (tmid.isEnabled()) midiInputs.add (tmid.createInstance (*
this));
554 midiDispatcher.setMidiDeviceList (midiOutputs);
560void EditPlaybackContext::removeInstanceForDevice (InputDevice& device)
565 for (
int i = midiInputs.size(); --i >= 0;)
566 if (&midiInputs.getUnchecked (i)->owner == &device)
567 removedInstances.
add (midiInputs.removeAndReturn (i));
569 for (
int i = waveInputs.size(); --i >= 0;)
570 if (&waveInputs.getUnchecked (i)->owner == &device)
571 removedInstances.
add (waveInputs.removeAndReturn (i));
573 if (! removedInstances.
isEmpty() && isAllocated)
579 jassert (getInputFor (&device) ==
nullptr);
581 if (waveInputs.add (device.createInstance (*
this)) !=
nullptr && isAllocated)
585void EditPlaybackContext::addMidiInputDeviceInstance (
InputDevice& device)
587 jassert (getInputFor (&device) ==
nullptr);
589 if (midiInputs.add (device.createInstance (*
this)) !=
nullptr && isAllocated)
593void EditPlaybackContext::clearNodes()
597 for (
auto mo : midiOutputs)
601 InputDeviceInstance::StopRecordingParameters params;
602 params.discardRecordings =
true;
604 for (
auto mi : midiInputs)
605 mi->stopRecording (params);
607 for (
auto wi : waveInputs)
608 wi->stopRecording (params);
611 priorityBooster =
nullptr;
616 if (nodePlaybackContext)
618 nodePlaybackContext->playHead.stop();
619 nodePlaybackContext->clearNode();
620 nodePlaybackContext->setNumThreads (0);
624void EditPlaybackContext::createNode()
632 auto& dm = edit.engine.getDeviceManager();
633 CreateNodeParams cnp { nodePlaybackContext->processState };
634 cnp.sampleRate = dm.getSampleRate();
635 cnp.blockSize = dm.getBlockSize();
637 if (cnp.sampleRate <= 0.0 || cnp.blockSize <= 0)
643 auto& engineBehaviour = edit.engine.getEngineBehaviour();
644 cnp.includeBypassedPlugins = ! engineBehaviour.shouldBypassedPluginsBeRemovedFromPlaybackGraph();
645 cnp.allowClipSlots = engineBehaviour.areClipSlotsEnabled();
646 cnp.readAheadTimeStretchNodes = engineBehaviour.enableReadAheadForTimeStretchNodes();
649 nodePlaybackContext->setNode (std::move (editNode), cnp.sampleRate, cnp.blockSize);
653void EditPlaybackContext::createPlayAudioNodes (TimePosition startTime)
656 startPlaying (startTime);
659void EditPlaybackContext::createPlayAudioNodesIfNeeded (TimePosition startTime)
662 createPlayAudioNodes (startTime);
665void EditPlaybackContext::reallocate()
667 createPlayAudioNodes (getPosition());
670void EditPlaybackContext::startPlaying (TimePosition start)
672 prepareOutputDevices (start);
674 if (priorityBooster ==
nullptr)
677 for (
auto mo : midiOutputs)
681juce::Result EditPlaybackContext::startRecording (TimePosition start, TimePosition punchIn)
683 struct InputAndContext
685 InputDeviceInstance* input =
nullptr;
686 InputDeviceInstance::PreparedContext preparedContext;
691 auto anyContextHasErrors = [&inputAndContexts]
693 for (
auto& inputAndContext : inputAndContexts)
694 if (
hasErrors (inputAndContext.preparedContext))
700 for (
int i = waveInputs.size(); --i >= 0 && ! anyContextHasErrors();)
701 if (
auto wi = waveInputs.getUnchecked (i))
702 if (wi->isRecordingActive())
705 for (
int i = midiInputs.size(); --i >= 0 && ! anyContextHasErrors();)
706 if (
auto mi = midiInputs.getUnchecked (i))
707 if (mi->isRecordingActive())
712 bool anyContexts =
false;
714 for (
auto& inputAndContext : inputAndContexts)
715 if (! inputAndContext.preparedContext.
empty())
723 for (
auto& inputAndContext : inputAndContexts)
724 for (auto& res : inputAndContext.preparedContext)
726 return
juce::Result::fail (res.error());
729 startPlaying (start);
731 for (
auto& inputAndContext : inputAndContexts)
733 if (! inputAndContext.input->isRecordingActive())
736 [[ maybe_unused ]]
auto [preparedContext, error] =
extract (std::move (inputAndContext.preparedContext));
737 [[ maybe_unused ]]
auto contextsLeft = inputAndContext.input->startRecording (std::move (preparedContext));
738 jassert (contextsLeft.empty());
744void EditPlaybackContext::prepareOutputDevices (TimePosition start)
747 auto& dm = edit.engine.getDeviceManager();
748 double sampleRate = dm.getSampleRate();
749 int blockSize = dm.getBlockSize();
755 start = globalStreamTimeToEditTime (start.inSeconds());
757 for (
auto wo : waveOutputs)
758 wo->prepareToPlay (sampleRate, blockSize);
760 for (
auto mo : midiOutputs)
761 mo->prepareToPlay (start, true);
763 midiDispatcher.prepareToPlay (start);
766void EditPlaybackContext::prepareForPlaying (TimePosition startTime)
768 createPlayAudioNodesIfNeeded (startTime);
771void EditPlaybackContext::prepareForRecording (TimePosition startTime, TimePosition punchIn)
773 createPlayAudioNodesIfNeeded (startTime);
774 startRecording (startTime, punchIn);
777static SelectionManager* findAppropriateSelectionManager (Edit& ed)
779 SelectionManager* found =
nullptr;
781 for (SelectionManager::Iterator iter; iter.next();)
782 if (
auto sm = iter.get())
783 if (sm->getEdit() == &ed)
784 if (found ==
nullptr || found->editViewID == -1)
790tl::expected<Clip::Array, juce::String> EditPlaybackContext::stopRecording (InputDeviceInstance& in,
bool discardRecordings)
792 TRACKTION_ASSERT_MESSAGE_THREAD
795 InputDeviceInstance::StopRecordingParameters params;
796 params.unloopedTimeToEndRecording = getUnloopedPosition();
797 params.isLooping = transport.looping;
799 params.discardRecordings = discardRecordings;
800 return in.stopRecording (params);
803tl::expected<Clip::Array, juce::String> EditPlaybackContext::stopRecording (TimePosition unloopedEnd,
bool discardRecordings)
805 TRACKTION_ASSERT_MESSAGE_THREAD
810 InputDeviceInstance::StopRecordingParameters params;
811 params.unloopedTimeToEndRecording = unloopedEnd;
812 params.isLooping = transport.looping;
814 params.discardRecordings = discardRecordings;
816 for (
auto in : getAllInputs())
817 in->stopRecording (params)
818 .map ([&] (auto c) { clips.addArray (std::move (c)); })
819 .map_error ([&] (
auto err) { error = err; });
822 return tl::unexpected (error);
829 TRACKTION_ASSERT_MESSAGE_THREAD
832 bool inputAssigned =
false;
834 for (
auto in : getAllInputs())
836 if (
isAttached (*in) && (! armedOnly || in->isRecordingActive()))
838 inputAssigned =
true;
844 return juce::Result::fail (
TRANS(
"Unable to perform retrospective record, no inputs are assigned to a track"));
846 InputDevice::setRetrospectiveLock (edit.engine, getAllInputs(),
true);
848 bool clipCreated =
false;
850 for (
auto in : getAllInputs())
852 for (
auto clip : in->applyRetrospectiveRecord (armedOnly))
854 if (clips !=
nullptr)
861 InputDevice::setRetrospectiveLock (edit.engine, getAllInputs(),
false);
866 if (clips !=
nullptr)
868 if (
auto sm = findAppropriateSelectionManager (edit))
871 sm->keepSelectedObjectsOnScreen();
888void EditPlaybackContext::fillNextNodeBlock (
float*
const* allChannels,
int numChannels,
int numSamples)
892 if (edit.isRendering())
895 SCOPED_REALTIME_CHECK
896 if (! nodePlaybackContext)
899 nodePlaybackContext->updateReferenceSampleRange (numSamples);
902 if (nodeContextToSyncTo && nodePlaybackContext->playHead.isPlaying() && nodeContextToSyncTo->getNodePlayHead() !=
nullptr)
905 jassert (nodeContextToSyncTo->getNodePlayHead() !=
nullptr);
906 const auto[action, newTimelineTime] = contextSyncroniser->getSyncAction (*nodeContextToSyncTo->getNodePlayHead(),
907 nodePlaybackContext->playHead,
908 nodeContextToSyncTo->getSampleRate());
918 nodePlaybackContext->postRollInToLoop (newTimelineTime.inSeconds());
923 nodeContextToSyncTo =
nullptr;
929 nodePlaybackContext->process (allChannels, numChannels, numSamples);
932 auto editTime = nodePlaybackContext->processState.getSyncRange().start.time;
933 midiDispatcher.dispatchPendingMessagesForDevices (editTime);
936InputDeviceInstance* EditPlaybackContext::getInputFor (InputDevice* d)
const
938 TRACKTION_ASSERT_MESSAGE_THREAD
940 for (
auto i : waveInputs)
944 for (
auto i : midiInputs)
951OutputDeviceInstance* EditPlaybackContext::getOutputFor (OutputDevice* d)
const
953 TRACKTION_ASSERT_MESSAGE_THREAD
955 for (
auto i : waveOutputs)
959 for (
auto i : midiOutputs)
966void EditPlaybackContext::syncToContext (EditPlaybackContext* newContextToSyncTo,
967 TimePosition newPreviousBarTime, TimeDuration newSyncInterval)
969 contextToSyncTo = newContextToSyncTo;
970 previousBarTime = newPreviousBarTime;
971 syncInterval = newSyncInterval;
974 if (newContextToSyncTo !=
nullptr && newContextToSyncTo->getNodePlayHead() !=
nullptr)
976 nodeContextToSyncTo = newContextToSyncTo;
977 contextSyncroniser->reset (previousBarTime, syncInterval);
981static bool hasCheckedDenormNoise =
false;
983bool EditPlaybackContext::shouldAddAntiDenormalisationNoise (
Engine& e)
985 static bool shouldAdd;
987 if (! hasCheckedDenormNoise)
989 shouldAdd = e.getPropertyStorage().getProperty (SettingID::addAntiDenormalNoise,
false);
990 hasCheckedDenormNoise =
true;
996void EditPlaybackContext::setAddAntiDenormalisationNoise (
Engine& e,
bool b)
998 e.getPropertyStorage().setProperty (SettingID::addAntiDenormalNoise, b);
999 hasCheckedDenormNoise =
false;
1006 return nodePlaybackContext ? &nodePlaybackContext->playHead
1010void EditPlaybackContext::blockUntilSyncPointChange()
1021 if (syncPointNow->referenceSamplePosition != startSyncPoint->referenceSamplePosition)
1034bool EditPlaybackContext::isPlaying()
const
1036 return nodePlaybackContext ? nodePlaybackContext->playHead.isPlaying()
1040bool EditPlaybackContext::isLooping()
const
1042 return nodePlaybackContext->playHead.isLooping();
1045bool EditPlaybackContext::isDragging()
const
1047 return nodePlaybackContext->playHead.isUserDragging();
1050TimePosition EditPlaybackContext::getPosition()
const
1052 return TimePosition::fromSamples (nodePlaybackContext->playHead.getPosition(),
1053 nodePlaybackContext->getSampleRate());
1056TimePosition EditPlaybackContext::getUnloopedPosition()
const
1058 return TimePosition::fromSamples (nodePlaybackContext->playHead.getUnloopedPosition(),
1059 nodePlaybackContext->getSampleRate());
1062TimeRange EditPlaybackContext::getLoopTimes()
const
1065 nodePlaybackContext->getSampleRate());
1068int EditPlaybackContext::getLatencySamples()
const
1070 return nodePlaybackContext ? nodePlaybackContext->getLatencySamples()
1074TimePosition EditPlaybackContext::getAudibleTimelineTime()
1076 return nodePlaybackContext ? TimePosition::fromSeconds (audiblePlaybackTime.load())
1077 : transport.getPosition();
1080double EditPlaybackContext::getSampleRate()
const
1082 return nodePlaybackContext ? nodePlaybackContext->getSampleRate()
1086void EditPlaybackContext::updateNumCPUs()
1088 if (nodePlaybackContext)
1089 nodePlaybackContext->setNumThreads ((
size_t) edit.engine.getEngineBehaviour().getNumberOfCPUsToUseForAudio() - 1);
1092void EditPlaybackContext::setSpeedCompensation (
double plusOrMinus)
1094 if (nodePlaybackContext)
1095 nodePlaybackContext->setSpeedCompensation (plusOrMinus);
1098void EditPlaybackContext::setTempoAdjustment (
double plusOrMinusProportion)
1100 if (nodePlaybackContext)
1101 nodePlaybackContext->setTempoAdjustment (plusOrMinusProportion);
1106 if (nodePlaybackContext)
1108 if (whenToJump && *whenToJump == positionToJumpTo)
1109 nodePlaybackContext->postPosition (positionToJumpTo, {});
1111 nodePlaybackContext->postPosition (positionToJumpTo, whenToJump);
1117 if (nodePlaybackContext)
1118 return nodePlaybackContext->getPendingPositionChange();
1123void EditPlaybackContext::play()
1125 if (nodePlaybackContext)
1126 nodePlaybackContext->playHead.play();
1129void EditPlaybackContext::stop()
1131 if (nodePlaybackContext)
1132 nodePlaybackContext->playHead.stop();
1137 if (nodePlaybackContext)
1138 return nodePlaybackContext->getSyncPoint();
1143TimePosition EditPlaybackContext::globalStreamTimeToEditTime (
double globalStreamTime)
const
1145 if (! nodePlaybackContext)
1148 const auto sampleRate = getSampleRate();
1149 const auto globalSamplePos = tracktion::graph::timeToSample (globalStreamTime, sampleRate);
1150 const auto timelinePosition = nodePlaybackContext->playHead.referenceSamplePositionToTimelinePosition (globalSamplePos);
1152 return TimePosition::fromSamples (timelinePosition, sampleRate);
1155TimePosition EditPlaybackContext::globalStreamTimeToEditTimeUnlooped (
double globalStreamTime)
const
1157 if (! nodePlaybackContext)
1160 const auto sampleRate = getSampleRate();
1161 const auto globalSamplePos = tracktion::graph::timeToSample (globalStreamTime, sampleRate);
1162 const auto timelinePosition = nodePlaybackContext->playHead.referenceSamplePositionToTimelinePositionUnlooped (globalSamplePos);
1164 return TimePosition::fromSamples (timelinePosition, sampleRate);
1167void EditPlaybackContext::resyncToGlobalStreamTime (
juce::Range<double> globalStreamTime,
double sampleRate)
1169 if (! nodePlaybackContext)
1172 const auto globalSampleRange = tracktion::graph::timeToSample (globalStreamTime, sampleRate);
1173 nodePlaybackContext->resyncToReferenceSampleRange (globalSampleRange);
1176void EditPlaybackContext::setThreadPoolStrategy (
int type)
1178 type =
juce::jlimit (
static_cast<int> (tracktion::graph::ThreadPoolStrategy::conditionVariable),
1179 static_cast<int> (tracktion::graph::ThreadPoolStrategy::lightweightSemHybrid),
1182 EditPlaybackContextInternal::getThreadPoolStrategyType() = type;
1185int EditPlaybackContext::getThreadPoolStrategy()
1187 const int type =
juce::jlimit (
static_cast<int> (tracktion::graph::ThreadPoolStrategy::conditionVariable),
1188 static_cast<int> (tracktion::graph::ThreadPoolStrategy::lightweightSemHybrid),
1189 EditPlaybackContextInternal::getThreadPoolStrategyType());
1194void EditPlaybackContext::enablePooledMemory (
bool enable)
1196 EditPlaybackContextInternal::getPooledMemoryFlag() = enable;
1199void EditPlaybackContext::enableNodeMemorySharing (
bool enable)
1201 EditPlaybackContextInternal::getNodeMemorySharingFlag() = enable;
1204void EditPlaybackContext::enableAudioWorkgroup (
bool enable)
1206 EditPlaybackContextInternal::getAudioWorkgroupFlag() = enable;
1209int EditPlaybackContext::getNumActivelyRecordingDevices()
const
1214void EditPlaybackContext::incrementNumActivelyRecordingDevices()
1219void EditPlaybackContext::decrementNumActivelyRecordingDevices()
1225static int numHighPriorityPlayers = 0, numRealtimeDefeaters = 0;
1227inline void updateProcessPriority (
Engine& engine)
1231 if (numHighPriorityPlayers > 0)
1232 level = numRealtimeDefeaters == 0 && engine.getPropertyStorage().getProperty (SettingID::useRealtime,
false) ? 2 : 1;
1234 engine.getEngineBehaviour().setProcessPriority (level);
1237EditPlaybackContext::ProcessPriorityBooster::ProcessPriorityBooster (
Engine& e) : engine (e) { ++numHighPriorityPlayers; updateProcessPriority (engine); }
1238EditPlaybackContext::ProcessPriorityBooster::~ProcessPriorityBooster() { --numHighPriorityPlayers; updateProcessPriority (engine); }
1239EditPlaybackContext::RealtimePriorityDisabler::RealtimePriorityDisabler (
Engine& e) : engine (e) { ++numRealtimeDefeaters; updateProcessPriority (engine); }
1240EditPlaybackContext::RealtimePriorityDisabler::~RealtimePriorityDisabler() { --numRealtimeDefeaters; updateProcessPriority (engine); }
void addArray(const Type *elementsToAdd, int numElementsToAdd)
void add(const ElementType &newElement)
void setSize(int newNumChannels, int newNumSamples, bool keepExistingContent=false, bool clearExtraSpace=false, bool avoidReallocating=false)
const Type * getReadPointer(int channelNumber) const noexcept
bool isEmpty() const noexcept
ObjectClass * add(ObjectClass *newObject)
static Range withStartAndLength(const ValueType startValue, const ValueType length) noexcept
constexpr ValueType getStart() const noexcept
constexpr ValueType getEnd() const noexcept
constexpr ValueType getLength() const noexcept
static Result fail(const String &errorMessage) noexcept
static Result ok() noexcept
bool isEmpty() const noexcept
static int getNumCpus() noexcept
static Time JUCE_CALLTYPE getCurrentTime() noexcept
void addWaveInputDeviceInstance(InputDevice &)
Note this doesn't check for device enablement.
static int getThreadPoolStrategy()
std::optional< SyncPoint > getSyncPoint() const
Returns the last reference sample position and the edit time and beat that it corresponded to.
AbletonLink & getAbletonLink() const noexcept
Returns the AbletonLink object.
TempoSequence tempoSequence
The global TempoSequence of this Edit.
void updateModifierTimers(TimePosition editTime, int numSamples) const
Updates all the ModifierTimers with a given edit time and number of samples.
Holds a list of TempoSetting objects, to form a sequence of tempo changes.
const tempo::Sequence & getInternalSequence() const
N.B.
Plays back a Node with PlayHeadState and ProcessState.
void setNumThreads(size_t numThreads)
Sets the number of threads to use for rendering.
void clearNode()
Clears the Node currently playing.
double getSampleRate() const
Returns the current sample rate.
int process(const tracktion::graph::Node::ProcessContext &pc)
Processes a block of audio and MIDI data.
Controls the transport of an Edit's playback.
TimePosition getPosition() const
Returns the current transport position.
TimeRange getLoopRange() const noexcept
Returns the loop range.
Struct to describe a single iteration of a process call.
Determines how this block releates to other previous render blocks and if the play head has jumped in...
Converts a monotonically increasing reference range in to a timeline range.
std::chrono::system_clock::time_point getLastUserInteractionTime() const
Returns the time of the last user interaction, either a setPosition or setUserIsDragging call.
int64_t getPosition() const
Returns the current timeline position.
void overridePosition(int64_t newPosition)
Adjust position without triggering a 'user interaction' change.
void setPosition(int64_t newPosition)
Sets the timeline position of the play head and if it is different logs a user interaction.
juce::Range< int64_t > getLoopRange() const noexcept
Returns the looped playback range.
void setReferenceSampleRange(juce::Range< int64_t > sampleRange)
Sets the reference sample count, adjusting the timeline if the play head is playing.
void setRollInToLoop(int64_t playbackPosition)
Puts the play head in to roll in to loop mode.
bool isPlaying() const noexcept
Returns true is the play head is currently playing.
bool isLooping() const noexcept
Returns true is the play head is in loop mode.
#define TRANS(stringLiteral)
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
int roundToInt(const FloatType value) noexcept
bool isAttached(InputDeviceInstance &instance)
Returns true if this input is assigned to a target.
juce::Array< AudioTrack * > getAudioTracks(const Edit &edit)
Returns all the AudioTracks in an Edit.
std::unique_ptr< tracktion::graph::Node > createNodeForEdit(EditPlaybackContext &epc, std::atomic< double > &audibleTimeToUpdate, const CreateNodeParams ¶ms)
Creates a Node to play back an Edit with live inputs and outputs.
bool hasErrors(const InputDeviceInstance::PreparedContext &pc)
Returns true if all the targets were fully prepared.
std::pair< std::vector< std::unique_ptr< InputDeviceInstance::RecordingContext > >, juce::StringArray > extract(InputDeviceInstance::PreparedContext &&pc)
Splits the PreparedContext in to valid RecordingContexts and an array of error messages.
TimePosition time
The Edit timeline time.
InputDeviceInstance::RecordingParameters getDefaultRecordingParameters(const EditPlaybackContext &context, TimePosition playStart, TimePosition punchIn)
Returns the default set of recording parameters.
Holds a reference sample position and the Edit time and beat that it corresponds to.
LockFreeMultiThreadedNodePlayer::ThreadPoolCreator getPoolCreatorFunction(ThreadPoolStrategy poolType)
Returns a function to create a ThreadPool for the given stategy.
TimeRange timeRangeFromSamples(juce::Range< int64_t > sampleRange, double sampleRate)
Creates a TimeRange from a range of samples.
ThreadPoolStrategy
Available strategies for thread pools.
constexpr TimePosition toPosition(TimeDuration)
Converts a TimeDuration to a TimePosition.
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
constexpr double sampleToTime(IntType samplePosition, double sampleRate)
Converts an integer sample number to a time in seconds.
Represents a position in beats.
Represents a duration in real-life time.
Represents a position in real-life time.
constexpr double inSeconds() const
Returns the TimePosition as a number of seconds.
@ rollInToLoop
Set the dest playhead to roll in to the loop.
@ breakSync
Break the sync and don't call this again.
Holds the state of a process call.
std::function< void()> onContinuityUpdated
Callback which can be set to be called when the continuity changes.
SyncPoint getSyncPoint() const
Returns the end of the SyncRange.
SyncRange getSyncRange() const
Returns the SyncRange for the current audio block.
void setPlaybackSpeedRatio(double newRatio)
Sets a playback speed ratio.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.