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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_Modifier.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//==============================================================================
16{
17public:
18 ValueFifo (double newSampleRate, TimeDuration maxNumSecondsToStore)
19 : sampleRate (newSampleRate)
20 {
21 jassert (sampleRate > 0.0);
22
23 const auto numSamplesToStore = (size_t) tracktion::toSamples (maxNumSecondsToStore, newSampleRate);
24 values = std::vector<float> (numSamplesToStore, 0.0f);
25 }
26
27 void addValue (int numSamplesDelta, float value)
28 {
29 jassert ((size_t) numSamplesDelta < values.size());
30
31 const float delta = (value - lastValue) / numSamplesDelta;
32 float alpha = lastValue;
33
34 while (--numSamplesDelta >= 0)
35 {
36 if (++headIndex == values.size())
37 headIndex = 0;
38
39 alpha += delta;
40 values[headIndex] = alpha;
41 }
42
43 lastValue = value;
44 }
45
46 [[nodiscard]] float getValueAt (TimeDuration numSeconds) const
47 {
48 const size_t sampleDelta = (size_t) tracktion::toSamples (numSeconds, sampleRate);
49
50 if (sampleDelta > values.size())
51 return {};
52
53 const int deltaIndex = int (headIndex) - int (sampleDelta);
54 const size_t valueIndex = (size_t) juce::negativeAwareModulo (deltaIndex, (int) values.size());
55
56 return values[valueIndex];
57 }
58
59 [[nodiscard]] std::vector<float> getValues (TimeDuration numSecondsBeforeNow) const
60 {
62
63 const auto numValues = std::min (values.size(),
64 (size_t) tracktion::toSamples (numSecondsBeforeNow, sampleRate));
65 v.reserve (numValues);
66
67 int index = (int) headIndex;
68
69 for (int i = (int) numValues; --i >= 0;)
70 {
71 v.push_back (values[(size_t) index]);
72
73 if (--index < 0)
74 index = (int) values.size() - 1;
75 }
76
77 return v;
78 }
79
80private:
81 double sampleRate = 0.0;
82 float lastValue = 0.0;
83 std::vector<float> values;
84 size_t headIndex = 0;
85};
86
87//==============================================================================
88//==============================================================================
90 : AutomatableEditItem (e, v),
91 state (v)
92{
93 auto um = &edit.getUndoManager();
94 colour.referTo (state, IDs::colour, um, juce::Colours::red.withHue (1.0f / 9.0f));
95 enabled.referTo (state, IDs::enabled, um, true);
96
97 enabledParam = new DiscreteLabelledParameter ("enabled", TRANS("Enabled"), *this, { 0.0f, 1.0f },
98 modifier::getEnabledNames().size(), modifier::getEnabledNames());
99 addAutomatableParameter (enabledParam);
100 enabledParam->attachToCurrentValue (enabled);
101
102 if (remapOnTempoChange.isUsingDefault())
103 remapOnTempoChange = true;
104}
105
107{
108 // Must remove parameters in sub-class!
109 jassert (getNumAutomatableParameters() == 0);
110}
111
113{
114 deselect();
115
116 for (auto p : getAllParametersBeingModifiedBy (edit, *this))
117 p->removeModifier (*this);
118
119 for (auto t : getAllTracks (edit))
120 t->hideAutomatableParametersForSource (itemID);
121
122 auto um = &edit.getUndoManager();
123 auto rack = state.getParent().getParent();
124
126
127 if (rack.hasType (IDs::RACK))
128 RackType::removeBrokenConnections (rack, um);
129}
130
131//==============================================================================
132void Modifier::baseClassInitialise (double newSampleRate, int blockSizeSamples)
133{
134 TRACKTION_ASSERT_MESSAGE_THREAD
135 sampleRate = newSampleRate;
136
137 if (initialiseCount++ == 0)
138 {
140 initialise (sampleRate, blockSizeSamples);
141
142 const auto numSecondsToStore = maxHistoryTime;
143 valueFifo = std::make_unique<ValueFifo> (sampleRate, numSecondsToStore);
144 messageThreadValueFifo = std::make_unique<ValueFifo> (sampleRate, numSecondsToStore);
145
146 const int numSamples = (int) tracktion::toSamples (numSecondsToStore, sampleRate);
147 const int numBlocks = (numSamples / blockSizeSamples) * 2;
148 valueFifoQueue.reset ((size_t) numBlocks);
149 }
150
153}
154
156{
157 TRACKTION_ASSERT_MESSAGE_THREAD
158 jassert (initialiseCount > 0);
159
160 if (--initialiseCount == 0)
161 {
163 deinitialise();
165 valueFifo.reset();
166 }
167}
168
170{
171 applyToBuffer (prc);
172 jassert (valueFifo);
173 const float v = getCurrentValue();
174 valueFifo->addValue (prc.bufferNumSamples, getCurrentValue());
175
176 // Queue a value change up to be dispatched on the message thread
177 const auto newV = std::make_pair (prc.bufferNumSamples, v);
178
179 if (valueFifoQueue.getFreeSlots() == 0)
180 {
181 std::remove_cv_t<decltype(newV)> temp;
182 valueFifoQueue.pop (temp);
183 }
184
185 valueFifoQueue.push (newV);
186}
187
188//==============================================================================
190{
191 return lastEditTime;
192}
193
194float Modifier::getValueAt (TimeDuration numSecondsBeforeNow) const
195{
196 if (valueFifo)
197 return valueFifo->getValueAt (numSecondsBeforeNow);
198
199 return {};
200}
201
203{
204 if (! messageThreadValueFifo)
205 return {};
206
207 // Dispatch pending values
208 for (;;)
209 {
211
212 if (! valueFifoQueue.pop (tmp))
213 break;
214
215 messageThreadValueFifo->addValue (tmp.first, tmp.second);
216 }
217
218 // Then return the array of samples
219 return messageThreadValueFifo->getValues (numSecondsBeforeNow);
220}
221
223{
224 for (auto p : getAutomatableParameters())
225 p->detachFromCurrentValue();
226
227 deleteAutomatableParameters();
228}
229
230//==============================================================================
232 : ValueTreeObjectList<Modifier> (parentTree),
233 edit (e), state (parent)
234{
235 jassert (parent.hasType (IDs::MODIFIERS));
236 callBlocking ([this] { rebuildObjects(); });
237}
238
240{
241 freeObjects();
242}
243
245{
246 return i == IDs::LFO || i == IDs::BREAKPOINTOSCILLATOR
247 || i == IDs::STEP || i == IDs::ENVELOPEFOLLOWER
248 || i == IDs::RANDOM || i == IDs::MIDITRACKER;
249}
250
252{
253 if (! edit.isLoading())
254 TRACKTION_ASSERT_MESSAGE_THREAD
255
257
258 for (auto m : objects)
259 mods.add (m);
260
261 return mods;
262}
263
264//==============================================================================
266{
267 TRACKTION_ASSERT_MESSAGE_THREAD
268 jassert (isSuitableType (v));
269 auto um = &edit.getUndoManager();
270
271 if (v.getParent().isValid())
272 v.getParent().removeChild (v, um);
273
274 const auto existing = objects[index];
275 const int indexBefore = existing == nullptr ? -1 : parent.indexOf (existing->state);
276 parent.addChild (v, indexBefore, um);
277
278 auto* modifier = [&]() -> Modifier*
279 {
280 for (auto o : objects)
281 if (o->state == v)
282 return o;
283
284 return {};
285 }();
286
287 jassert (modifier != nullptr);
288
289 if (sm != nullptr)
290 sm->selectOnly (modifier);
291
292 return modifier;
293}
294
295//==============================================================================
296bool ModifierList::isSuitableType (const juce::ValueTree& v) const
297{
298 return isModifier (v.getType());
299}
300
301Modifier* ModifierList::createNewObject (const juce::ValueTree& v)
302{
304 auto m = findModifierForID (edit, EditItemID::readOrCreateNewID (edit, v));
305
306 if (m == nullptr)
307 {
308 if (v.hasType (IDs::LFO)) m = new LFOModifier (edit, v);
309 else if (v.hasType (IDs::BREAKPOINTOSCILLATOR)) m = new BreakpointOscillatorModifier (edit, v);
310 else if (v.hasType (IDs::STEP)) m = new StepModifier (edit, v);
311 else if (v.hasType (IDs::ENVELOPEFOLLOWER)) m = new EnvelopeFollowerModifier (edit, v);
312 else if (v.hasType (IDs::RANDOM)) m = new RandomModifier (edit, v);
313 else if (v.hasType (IDs::MIDITRACKER)) m = new MIDITrackerModifier (edit, v);
314 else jassertfalse;
315
316 m->initialise();
317 }
318
319 m->incReferenceCount();
320
321 return m.get();
322}
323
324void ModifierList::deleteObject (Modifier* m)
325{
326 jassert (m != nullptr);
327 m->decReferenceCount();
328}
329
330void ModifierList::newObjectAdded (Modifier*) { sendChangeMessage(); }
331void ModifierList::objectRemoved (Modifier*) { sendChangeMessage(); }
332void ModifierList::objectOrderChanged() { sendChangeMessage(); }
333
334//==============================================================================
335Modifier::Ptr findModifierForID (ModifierList& ml, EditItemID modifierID)
336{
337 for (auto mod : ml.getModifiers())
338 if (mod->itemID == modifierID)
339 return mod;
340
341 return {};
342}
343
344}} // namespace tracktion { inline namespace engine
bool isUsingDefault() const
void referTo(ValueTree &tree, const Identifier &property, UndoManager *um)
bool isValid() const noexcept
ObjectClass * add(ObjectClass *newObject)
void removeChild(const ValueTree &child, UndoManager *undoManager)
ValueTree getParent() const noexcept
Base class for elements that have some kind of automatable parameters.
void resetRecordingStatus()
Marks the end of an automation recording stream.
const EditItemID itemID
Every EditItem has an ID which is unique within the edit.
The Tracktion Edit class!
bool isLoading() const
Returns true if the Edit's not yet fully loaded.
juce::UndoManager & getUndoManager() noexcept
Returns the juce::UndoManager used for this Edit.
Holds a list of Modifiers that have been added to a Track.
static bool isModifier(const juce::Identifier &)
Tests whether the Identifier is of a known Modifier type.
ModifierList(Edit &, const juce::ValueTree &)
Creates a ModifierList for an Edit and given state.
juce::ReferenceCountedObjectPtr< Modifier > insertModifier(juce::ValueTree, int index, SelectionManager *)
Adds a Modifier from a state at a given index.
juce::ReferenceCountedArray< Modifier > getModifiers() const
Returns all the Modifiers in the list.
Manages a list of items that are currently selected.
T is_pointer_v
#define TRANS(stringLiteral)
#define jassert(expression)
#define jassertfalse
typedef int
T make_pair(T... args)
T min(T... args)
IntegerType negativeAwareModulo(IntegerType dividend, const IntegerType divisor) noexcept
juce::ReferenceCountedArray< AutomatableParameter > getAllParametersBeingModifiedBy(Edit &edit, AutomatableParameter::ModifierSource &m)
Iterates an Edit looking for all parameters that are being modified by the given ModifierSource.
juce::Array< Track * > getAllTracks(const Edit &edit)
Returns all the tracks in an Edit.
Modifier::Ptr findModifierForID(ModifierList &ml, EditItemID modifierID)
Returns a Modifier if it can be found in the list.
T size(T... args)
Represents a duration in real-life time.
Represents a position in real-life time.
ID for objects of type EditElement - e.g.
Bass class for parameter Modifiers.
virtual float getCurrentValue()=0
Must return the current value of the modifier.
void selectableAboutToBeDeleted() override
Called just before the selectable is about to be deleted so any subclasses should still be valid at t...
juce::ValueTree state
Modifier internal state.
void baseClassApplyToBuffer(const PluginRenderContext &)
Updates internal value history and calls the subclass's applyToBuffer method.
virtual void applyToBuffer(const PluginRenderContext &)
Sub classes should implement this to process the Modifier.
TimePosition getCurrentTime() const
Returns the edit time of the current value.
virtual void deinitialise()
Sub classes should implement this to deinitialise the Modifier.
void baseClassInitialise(double sampleRate, int blockSizeSamples)
Initialises the Modifier.
static constexpr TimeDuration maxHistoryTime
The max number of seconds of modifier value history that is stored.
virtual void initialise()=0
Call this once after construction to connect it to the audio graph.
juce::CachedValue< juce::Colour > colour
Colour property.
AutomatableParameter::Ptr enabledParam
Parameter to change the enabled state.
void remove()
Removes this Modifier from its parent Track.
juce::CachedValue< float > enabled
Enabled property.
void baseClassDeinitialise()
Deinitialises the Modifier.
Modifier(Edit &, const juce::ValueTree &)
Creates a Modifier for a given state.
float getValueAt(TimeDuration numSecondsBeforeNow) const
Returns the value of the at a given time in the past.
std::vector< float > getValues(TimeDuration numSecondsBeforeNow) const
Returns a vector of previous sample values.
The context passed to plugin render methods to provide it with buffers to fill.
int bufferNumSamples
The number of samples to write into the audio buffer.
typedef size_t
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.