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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_MelodyneNode.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
14//==============================================================================
15//==============================================================================
20{
21public:
23 : plugin (p)
24 {
25 }
26
28 void setCurrentInfo (TimePosition currentTimeSeconds, bool playing,
29 bool looping, TimeRange loopTimes)
30 {
31 time = currentTimeSeconds;
32 isPlaying = playing;
33 isLooping = looping;
34 loopTimeRange = loopTimes;
35
36 loopStart.set (loopTimeRange.getStart());
37 loopEnd.set (loopTimeRange.getEnd());
38 currentPos.set (time);
39 }
40
41 juce::Optional<PositionInfo> getPosition() const override
42 {
43 PositionInfo result;
44
45 result.setFrameRate (getFrameRate());
46
47 auto& transport = plugin.edit.getTransport();
48
49 result.setIsPlaying (isPlaying);
50 result.setIsLooping (isLooping);
51 result.setIsRecording (transport.isRecording());
52 result.setEditOriginTime (transport.getTimeWhenStarted().inSeconds());
53
54 if (isLooping)
55 result.setLoopPoints (juce::AudioPlayHead::LoopPoints ({ loopStart.getPPQTime(), loopEnd.getPPQTime() }));
56
57 result.setTimeInSeconds (time.inSeconds());
58 result.setTimeInSamples (toSamples (time, plugin.getAudioPluginInstance()->getSampleRate()));
59
60 const auto timeSig = currentPos.getTimeSignature();
61 result.setBpm (currentPos.getTempo());
62 result.setTimeSignature (juce::AudioPlayHead::TimeSignature ({ timeSig.numerator, timeSig.denominator }));
63
64 const auto ppqPositionOfLastBarStart = currentPos.getPPQTimeOfBarStart();
65 result.setPpqPositionOfLastBarStart (ppqPositionOfLastBarStart);
66 result.setPpqPosition (std::max (ppqPositionOfLastBarStart, currentPos.getPPQTime()));
67
68 return result;
69 }
70
71private:
72 ExternalPlugin& plugin;
73 tempo::Sequence::Position currentPos { createPosition (plugin.edit.tempoSequence) };
74 tempo::Sequence::Position loopStart { createPosition (plugin.edit.tempoSequence) };
75 tempo::Sequence::Position loopEnd { createPosition (plugin.edit.tempoSequence) };
76 TimePosition time;
77 bool isPlaying = false, isLooping = false;
78 TimeRange loopTimeRange;
79
80 AudioPlayHead::FrameRateType getFrameRate() const
81 {
82 switch (plugin.edit.getTimecodeFormat().getFPS())
83 {
84 case 24: return AudioPlayHead::fps24;
85 case 25: return AudioPlayHead::fps25;
86 case 29: return AudioPlayHead::fps30drop;
87 case 30: return AudioPlayHead::fps30;
88 default: break;
89 }
90
91 return AudioPlayHead::fps30; //Just to cope with it.
92 }
93
95};
96
97//==============================================================================
98//==============================================================================
99MelodyneNode::MelodyneNode (AudioClipBase& c, tracktion::graph::PlayHead& ph, bool isRendering)
100 : clip (c), playHead (ph),
101 clipLevel (clip.getLiveClipLevel()), clipPtr (&c),
102 melodyneProxy (c.melodyneProxy),
103 fileInfo (clip.getAudioFile().getInfo()),
104 isOfflineRender (isRendering)
105{
106 // Only need to check the analysing state when we're rendering
107 if (isOfflineRender)
108 {
109 updateAnalysingState();
110
111 if (analysingContent)
112 startTimerHz (10);
113 }
114}
115
116MelodyneNode::~MelodyneNode()
117{
119
120 if (auto ep = melodyneProxy->getPlugin())
121 if (auto p = ep->getAudioPluginInstance())
122 p->setPlayHead (nullptr);
123
124 playhead.reset();
125 melodyneProxy = nullptr;
126}
127
128//==============================================================================
130{
132 props.hasAudio = true;
133 props.numberOfChannels = fileInfo.numChannels;
134
135 if (auto plugin = melodyneProxy->getPlugin())
136 if (auto p = plugin->getAudioPluginInstance())
137 props.numberOfChannels = juce::jmax (props.numberOfChannels, p->getTotalNumInputChannels(), p->getTotalNumOutputChannels());
138
139 return props;
140}
141
146
148{
150 if (auto plugin = melodyneProxy->getPlugin())
151 {
152 if (auto p = plugin->getAudioPluginInstance())
153 {
154 //NB: Reducing the number of calls to juce::AudioProcessor::prepareToPlay() keeps Celemony happy;
155 // the VST3 version of their plugin relies heavily on calls to the inconspicuous IComponent::setActive()!
156 if (p->getSampleRate() != info.sampleRate
157 || p->getBlockSize() != info.blockSize)
158 {
159 plugin->initialise ({ TimePosition(), info.sampleRate, info.blockSize });
160 }
161
162 p->setPlayHead (nullptr);
163 playhead = std::make_unique<MelodynePlayhead> (*plugin);
164 p->setPlayHead (playhead.get());
165
166 desc = p->getPluginDescription();
167 }
168 }
169}
170
172{
173 if (! isOfflineRender)
174 return true;
175
176 return ! analysingContent;
177}
178
180{
182 auto& dest = pc.buffers.audio;
183
184 if (dest.getNumFrames() == 0 || dest.getNumChannels() == 0 || melodyneProxy == nullptr)
185 return;
186
187 if (auto plugin = melodyneProxy->getPlugin())
188 {
189 if (auto pluginInstance = plugin->getAudioPluginInstance())
190 {
191 if (pluginInstance->getPlayHead() != playhead.get())
192 pluginInstance->setPlayHead (playhead.get()); //This is needed in case the ExternalPlugin back-end has swapped the playhead out
193
194 midiMessages.clear();
195
196 // Update PlayHead
197 {
198 const auto timelinePosition = referenceSampleRangeToSplitTimelineRange (playHead, pc.referenceSampleRange).timelineRange1.getStart();
199 const auto loopPositions = playHead.getLoopRange();
200 const auto sampleRate = pluginInstance->getSampleRate();
201 playhead->setCurrentInfo (TimePosition::fromSamples (timelinePosition, sampleRate),
202 playHead.isPlaying(), playHead.isLooping(),
203 tracktion::timeRangeFromSamples (loopPositions, sampleRate));
204 }
205
206 auto asb = tracktion::graph::toAudioBuffer (dest);
207 pluginInstance->processBlock (asb, midiMessages);
208
209 float gains[2];
210
211 if (asb.getNumChannels() > 1)
212 clipLevel.getLeftAndRightGains (gains[0], gains[1]);
213 else
214 gains[0] = gains[1] = clipLevel.getGainIncludingMute();
215
216 if (playHead.isUserDragging())
217 {
218 gains[0] *= 0.4f;
219 gains[1] *= 0.4f;
220 }
221
222 for (int i = 0; i < asb.getNumChannels(); ++i)
223 {
224 const float gain = gains[i & 1];
225
226 if (gain != 1.0f)
227 asb.applyGain (i, 0, asb.getNumSamples(), gain);
228 }
229 }
230 }
231}
232
233//==============================================================================
234void MelodyneNode::updateAnalysingState()
235{
236 TRACKTION_ASSERT_MESSAGE_THREAD
237 analysingContent = melodyneProxy->isAnalysingContent();
238
239 if (! analysingContent)
240 stopTimer();
241}
242
243void MelodyneNode::timerCallback()
244{
245 updateAnalysingState();
246}
247
248}} // namespace tracktion { inline namespace engine
double getSampleRate() const noexcept
void clear() noexcept
constexpr ValueType getStart() const noexcept
void stopTimer() noexcept
void startTimerHz(int timerFrequencyHz) noexcept
TransportControl & getTransport() const noexcept
Returns the TransportControl which is used to stop/stop/position playback and recording.
This class is a necessary bodge due to ARA needing to be told that we're playing, even if we aren't,...
void setCurrentInfo(TimePosition currentTimeSeconds, bool playing, bool looping, TimeRange loopTimes)
Must be called before processing audio/MIDI.
tracktion::graph::NodeProperties getNodeProperties() override
Should return the properties of the node.
std::vector< Node * > getDirectInputNodes() override
Should return all the inputs directly feeding in to this node.
void process(ProcessContext &) override
Called when the node is to be processed.
void prepareToPlay(const tracktion::graph::PlaybackInitialisationInfo &) override
Called once before playback begins for each node.
bool isReadyToProcess() override
Should return true when this node is ready to be processed.
Struct to describe a single iteration of a process call.
Converts a monotonically increasing reference range in to a timeline range.
bool isUserDragging() const
Returns true if the user is dragging.
juce::Range< int64_t > getLoopRange() const noexcept
Returns the looped playback range.
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.
T is_pointer_v
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
T max(T... args)
constexpr Type jmax(Type a, Type b)
tempo::Sequence::Position createPosition(const TempoSequence &s)
Creates a Position to iterate over the given TempoSequence.
SplitTimelineRange referenceSampleRangeToSplitTimelineRange(const PlayHead &playHead, juce::Range< int64_t > referenceSampleRange)
Converts a reference sample range to a TimelinePositionWindow which could have two time ranges if the...
RangeType< TimePosition > TimeRange
A RangeType based on real time (i.e.
Represents a position in real-life time.
constexpr double inSeconds() const
Returns the TimePosition as a number of seconds.
double getPPQTimeOfBarStart() const noexcept
Returns the PPQ start time of the bar the position is in.
double getPPQTime() const noexcept
Returns the position as a PPQ time.
TimeSignature getTimeSignature() const
Returns the current TimeSignature of the Position.
void set(TimePosition)
Sets the Position to a new time.
double getTempo() const
Returns the current tempo of the Position.
float getGainIncludingMute() const noexcept
Returns the clip's gain if the clip is not muted.
void getLeftAndRightGains(float &left, float &right) const noexcept
Reutrns the left and right gains taking in to account mute and pan values.
Holds some really basic properties of a node.
Passed into Nodes when they are being initialised, to give them useful contextual information that th...
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.