tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_ExternalPlayheadSynchroniser.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
15{
17 auto& transport = edit.getTransport();
18 auto& tempoSequence = edit.tempoSequence;
19
20 auto currentTime = transport.getPosition();
21
22 if (auto epc = transport.getCurrentPlaybackContext())
23 currentTime = epc->getPosition();
24
25 auto position = createPosition (tempoSequence);
26 position.set (currentTime);
27 const auto timeSig = position.getTimeSignature();
28
29 info.bpm = position.getTempo();
30 info.timeSigNumerator = timeSig.numerator;
31 info.timeSigDenominator = timeSig.denominator;
32 info.timeInSeconds = position.getTime().inSeconds();
33 info.editOriginTime = 0.0;
34 info.ppqPosition = position.getPPQTime();
35 info.ppqPositionOfLastBarStart = position.getPPQTimeOfBarStart();
36 info.frameRate = juce::AudioPlayHead::fpsUnknown;
37 info.isPlaying = transport.isPlaying();
38 info.isRecording = transport.isRecording();
39 info.isLooping = transport.looping;
40
41 if (auto epc = transport.getCurrentPlaybackContext())
42 info.isPlaying = epc->isPlaying();
43
44 info.timeInSamples = (int64_t) std::floor ((info.timeInSeconds * edit.engine.getDeviceManager().getSampleRate()) + 0.5);
45
46 const auto loopRange = transport.getLoopRange();
47 position.set (loopRange.getStart());
48 info.ppqLoopStart = position.getPPQTime();
49 position.set (loopRange.getEnd());
50 info.ppqLoopEnd = position.getPPQTime();
51
52 return info;
53}
54
55//==============================================================================
57{
58 if (info.bpm == 0.0)
59 return;
60
61 auto& transport = edit.getTransport();
62
63 // First synchronise position
64 if (auto epc = transport.getCurrentPlaybackContext())
65 {
66 // N.B. Because we don't have full tempo sequence info from the host, we have
67 // to assume that the tempo is constant and just sync to that
68 // We could sync to a single bar by subtracting the ppqPositionOfLastBarStart from ppqPosition here
69 const double beatsSinceStart = (info.ppqPosition * info.timeSigDenominator) / 4.0;
70 const double secondsPerBeat = 240.0 / (info.bpm * info.timeSigDenominator);
71 const TimeDuration timeOffset = TimeDuration::fromSeconds (beatsSinceStart * secondsPerBeat);
72
73 const TimeDuration blockSizeInSeconds = TimeDuration::fromSeconds (edit.engine.getDeviceManager().getBlockSizeMs() / 1000.0);
74 const TimePosition currentPositionInSeconds = epc->getPosition() + blockSizeInSeconds;
75 // N.B we add the blockSizeInSeconds here as the playhead position will be at the end of the last block.
76 // This avoids us re-syncing every block
77
78 if (info.isPlaying)
79 {
80 if (TimeDuration::fromSeconds (std::abs ((currentPositionInSeconds - timeOffset).inSeconds())) > (blockSizeInSeconds / 2.0))
81 epc->postPosition (toPosition (timeOffset));
82
83 if (! epc->isPlaying())
84 epc->play();
85 }
86 else
87 {
88 transport.setPosition (toPosition (timeOffset));
89
90 if (epc->isPlaying())
91 epc->stop();
92 }
93 }
94
95 // Then the tempo info
96 {
97 auto position = createPosition (edit.tempoSequence);
98 position.set (TimePosition::fromSeconds (info.timeInSeconds));
99 const auto tempo = position.getTempo();
100 const auto timeSig = position.getTimeSignature();
101
102 const auto newBpm = info.bpm;
103 const auto newNumerator = info.timeSigNumerator;
104 const auto newDenominator = info.timeSigDenominator;
105
106 if (tempo != newBpm
107 || timeSig.numerator != newNumerator
108 || timeSig.denominator != newDenominator)
109 {
110 juce::MessageManager::callAsync ([editRef = makeSafeRef (edit), newBpm, newNumerator, newDenominator]
111 {
112 if (auto ed = editRef.get())
113 {
114 // N.B. This assumes only a simple tempo sequence with a single point
115 auto& tempoSequence = ed->tempoSequence;
116 auto tempoSetting = tempoSequence.getTempo (0);
117 auto timeSigSetting = tempoSequence.getTimeSig (0);
118
119 tempoSetting->setBpm (newBpm);
120 timeSigSetting->numerator = newNumerator;
121 timeSigSetting->denominator = newDenominator;
122 }
123 });
124 }
125 }
126}
127
128
129//==============================================================================
130//==============================================================================
132 : edit (e)
133{
134 positionInfo.resetToDefault();
135}
136
138{
139 if (positionInfoLock.tryEnter())
140 {
141 const auto pos = playhead.getPosition();
142 const bool sucess = pos.hasValue();
143
144 if (sucess)
145 {
146 positionInfo.resetToDefault();
147
148 if (const auto sig = pos->getTimeSignature())
149 {
150 positionInfo.timeSigNumerator = sig->numerator;
151 positionInfo.timeSigDenominator = sig->denominator;
152 }
153
154 if (const auto loop = pos->getLoopPoints())
155 {
156 positionInfo.ppqLoopStart = loop->ppqStart;
157 positionInfo.ppqLoopEnd = loop->ppqEnd;
158 }
159
160 if (const auto frame = pos->getFrameRate())
161 positionInfo.frameRate = *frame;
162
163 if (const auto timeInSeconds = pos->getTimeInSeconds())
164 positionInfo.timeInSeconds = *timeInSeconds;
165
166 if (const auto lastBarStartPpq = pos->getPpqPositionOfLastBarStart())
167 positionInfo.ppqPositionOfLastBarStart = *lastBarStartPpq;
168
169 if (const auto ppqPosition = pos->getPpqPosition())
170 positionInfo.ppqPosition = *ppqPosition;
171
172 if (const auto originTime = pos->getEditOriginTime())
173 positionInfo.editOriginTime = *originTime;
174
175 if (const auto bpm = pos->getBpm())
176 positionInfo.bpm = *bpm;
177
178 if (const auto timeInSamples = pos->getTimeInSamples())
179 positionInfo.timeInSamples = *timeInSamples;
180
181 positionInfo.isPlaying = pos->getIsPlaying();
182 positionInfo.isRecording = pos->getIsRecording();
183 positionInfo.isLooping = pos->getIsLooping();
184 }
185
186 positionInfoLock.exit();
187
188 if (sucess)
189 {
190 // Synchronise the Edit's position and tempo info based on the host
191 synchroniseEditPosition (edit, positionInfo);
192
193 return true;
194 }
195 }
196
197 return false;
198}
199
201{
202 if (auto playhead = processor.getPlayHead())
203 return synchronise (*playhead);
204
205 return false;
206}
207
213
214}} // namespace tracktion { inline namespace engine
virtual Optional< PositionInfo > getPosition() const=0
AudioPlayHead * getPlayHead() const noexcept
void exit() const noexcept
bool tryEnter() const noexcept
The Tracktion Edit class!
TransportControl & getTransport() const noexcept
Returns the TransportControl which is used to stop/stop/position playback and recording.
TempoSequence tempoSequence
The global TempoSequence of this Edit.
Engine & engine
A reference to the Engine.
DeviceManager & getDeviceManager() const
Returns the DeviceManager instance for handling audio / MIDI devices.
ExternalPlayheadSynchroniser(Edit &)
Constructs an ExternalPlayheadSynchroniser.
juce::AudioPlayHead::CurrentPositionInfo getPositionInfo() const
Returns the current position info, useful for graphical displays etc.
bool synchronise(juce::AudioPlayHead &)
Synchronises the Edit's playback position with an AudioPlayHead if possible.
T floor(T... args)
tempo::Sequence::Position createPosition(const TempoSequence &s)
Creates a Position to iterate over the given TempoSequence.
void synchroniseEditPosition(Edit &edit, const juce::AudioPlayHead::CurrentPositionInfo &info)
Syncs an Edit's transport and tempo sequence to a juce AudioPlayHead.
SafeSelectable< SelectableType > makeSafeRef(SelectableType &selectable)
Creates a SafeSelectable for a given selectable object.
juce::AudioPlayHead::CurrentPositionInfo getCurrentPositionInfo(Edit &edit)
Converts an Edit's internal transport information to a juce::AudioPlayHead::CurrentPositionInfo.
typedef int64_t
Represents a duration in real-life time.
Represents a position in real-life time.