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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_RandomModifier.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 : modifier (rm)
18 {
19 }
20
21 void updateStreamTime (TimePosition editTime, int numSamples) override
22 {
23 const double blockLength = numSamples / modifier.getSampleRate();
24 modifier.setEditTime (editTime);
25 modifier.updateParameterStreams (editTime);
26
27 const auto syncTypeThisBlock = juce::roundToInt (modifier.syncTypeParam->getCurrentValue());
28 const auto rateTypeThisBlock = getTypedParamValue<ModifierCommon::RateType> (*modifier.rateTypeParam);
29
30 const float rateThisBlock = modifier.rateParam->getCurrentValue();
31
32 if (rateTypeThisBlock == ModifierCommon::hertz)
33 {
34 const float durationPerPattern = 1.0f / rateThisBlock;
35 ramp.setDuration (durationPerPattern);
36
37 if (syncTypeThisBlock == ModifierCommon::transport)
38 ramp.setPosition (std::fmod ((float) editTime.inSeconds(), durationPerPattern));
39
40 modifier.setPhase (ramp.getProportion());
41
42 // Move the ramp on for the next block
43 ramp.process ((float) blockLength);
44 }
45 else
46 {
47 tempoSequence.set (editTime);
48 const auto currentTempo = tempoSequence.getTempo();
49 const auto currentTimeSig = tempoSequence.getTimeSignature();
50 const auto proportionOfBar = ModifierCommon::getBarFraction (rateTypeThisBlock);
51
52 if (syncTypeThisBlock == ModifierCommon::transport)
53 {
54 const auto editTimeInBeats = tempoSequence.getBeats().inBeats();
55 const auto bars = (editTimeInBeats / currentTimeSig.numerator) * rateThisBlock;
56
57 if (rateTypeThisBlock >= ModifierCommon::fourBars && rateTypeThisBlock <= ModifierCommon::sixtyFourthD)
58 {
59 const double virtualBars = bars / proportionOfBar;
60 modifier.setPhase ((float) std::fmod (virtualBars, 1.0f));
61 }
62 }
63 else
64 {
65 const double bpm = (currentTempo * rateThisBlock) / proportionOfBar;
66 const double secondsPerBeat = 60.0 / bpm;
67 const float secondsPerStep = static_cast<float> (secondsPerBeat * currentTimeSig.numerator);
68 const float secondsPerPattern = secondsPerStep;
69 ramp.setDuration (secondsPerPattern);
70
71 modifier.setPhase (ramp.getProportion());
72
73 // Move the ramp on for the next block
74 ramp.process ((float) blockLength);
75 }
76 }
77 }
78
79 void resync (double duration)
80 {
81 if (juce::roundToInt (modifier.syncTypeParam->getCurrentValue()) == ModifierCommon::note)
82 {
83 ramp.setPosition (0.0f);
84 modifier.setPhase (0.0f);
85
86 // Move the ramp on for the next block
87 ramp.process ((float) duration);
88 }
89 }
90
91 RandomModifier& modifier;
92 Ramp ramp;
93 tempo::Sequence::Position tempoSequence { createPosition (modifier.edit.tempoSequence) };
94
96};
97
98//==============================================================================
99RandomModifier::RandomModifier (Edit& e, const juce::ValueTree& v)
100 : Modifier (e, v)
101{
102 auto um = &edit.getUndoManager();
103
104 type.referTo (state, IDs::type, um, random);
105 shape.referTo (state, IDs::shape, um);
106 syncType.referTo (state, IDs::syncType, um, float (ModifierCommon::free));
107 rate.referTo (state, IDs::rate, um, 1.0f);
108 rateType.referTo (state, IDs::rateType, um, float (ModifierCommon::bar));
109 depth.referTo (state, IDs::depth, um, 1.0f);
110 stepDepth.referTo (state, IDs::stepDepth, um, 1.0f);
111 smooth.referTo (state, IDs::smooth, um);
112 bipolar.referTo (state, IDs::bipolar, um);
113
114 auto addDiscreteParam = [this] (const juce::String& paramID, const juce::String& name,
116 const juce::StringArray& labels) -> AutomatableParameter*
117 {
118 auto* p = new DiscreteLabelledParameter (paramID, name, *this, valueRange, labels.size(), labels);
119 addAutomatableParameter (p);
120 p->attachToCurrentValue (val);
121
122 return p;
123 };
124
125 auto addParam = [this] (const juce::String& paramID, const juce::String& name,
127 float centreVal, juce::CachedValue<float>& val,
128 const juce::String& suffix) -> AutomatableParameter*
129 {
130 valueRange.setSkewForCentre (centreVal);
131 auto* p = new SuffixedParameter (paramID, name, *this, valueRange, suffix);
132 addAutomatableParameter (p);
133 p->attachToCurrentValue (val);
134
135 return p;
136 };
137
138 using namespace ModifierCommon;
139 typeParam = addDiscreteParam ("type", TRANS("Type"), { 0.0f, (float) getTypeNames().size() - 1 }, type, getTypeNames());
140 shapeParam = addParam ("shape", TRANS("Shape"), { 0.0f, 1.0f }, 0.5f, shape, {});
141 syncTypeParam = addDiscreteParam ("syncType", TRANS("Sync type"), { 0.0f, (float) getSyncTypeChoices().size() - 1 }, syncType, getSyncTypeChoices());
142 rateParam = addParam ("rate", TRANS("Rate"), { 0.01f, 50.0f }, 1.0f, rate, {});
143 rateTypeParam = addDiscreteParam ("rateType", TRANS("Rate Type"), { 0.0f, (float) getRateTypeChoices().size() - 1 }, rateType, getRateTypeChoices());
144 depthParam = addParam ("depth", TRANS("Depth"), { 0.0f, 1.0f }, 0.5f, depth, {});
145 stepDepthParam = addParam ("stepDepth", TRANS("Step Depth"),{ 0.0f, 1.0f }, 0.5f, stepDepth, {});
146 smoothParam = addParam ("smooth", TRANS("Smooth"), { 0.0f, 1.0f }, 0.5f, smooth, {});
147 bipolarParam = addDiscreteParam ("biopolar", TRANS("Bipoloar"), { 0.0f, 1.0f }, bipolar, { NEEDS_TRANS("Uni-polar"), NEEDS_TRANS("Bi-polar") });
148
149 changedTimer.setCallback ([this]
150 {
151 changedTimer.stopTimer();
152 changed();
153 });
154
155 state.addListener (this);
156}
157
158RandomModifier::~RandomModifier()
159{
160 state.removeListener (this);
161 notifyListenersOfDeletion();
162
163 edit.removeModifierTimer (*modifierTimer);
164
165 for (auto p : getAutomatableParameters())
166 p->detachFromCurrentValue();
167
168 deleteAutomatableParameters();
169}
170
172{
173 // Do this here in case the audio code starts using the parameters before the constructor has finished
174 modifierTimer = std::make_unique<RandomModifierTimer> (*this);
175 edit.addModifierTimer (*modifierTimer);
176
178}
179
180//==============================================================================
182{
183 return currentValue.load (std::memory_order_acquire);
184}
185
187{
188 return currentPhase.load (std::memory_order_acquire);
189}
190
195
197{
198 if (prc.bufferForMidiMessages == nullptr)
199 return;
200
201 for (auto& m : *prc.bufferForMidiMessages)
202 if (m.isNoteOn())
203 modifierTimer->resync (prc.bufferNumSamples / getSampleRate());
204}
205
206//==============================================================================
207RandomModifier::Assignment::Assignment (const juce::ValueTree& v, const RandomModifier& rm)
208 : AutomatableParameter::ModifierAssignment (rm.edit, v),
209 randomModifierID (rm.itemID)
210{
211}
212
213bool RandomModifier::Assignment::isForModifierSource (const ModifierSource& source) const
214{
215 if (auto* mod = dynamic_cast<const RandomModifier*> (&source))
216 return mod->itemID == randomModifierID;
217
218 return false;
219}
220
221RandomModifier::Ptr RandomModifier::Assignment::getRandomModifier() const
222{
223 if (auto mod = findModifierTypeForID<RandomModifier> (edit, randomModifierID))
224 return mod;
225
226 return {};
227}
228
229//==============================================================================
230juce::StringArray RandomModifier::getTypeNames()
231{
232 return { NEEDS_TRANS("Random"),
233 NEEDS_TRANS("Noise") };
234}
235
236//==============================================================================
237void RandomModifier::setPhase (float newPhase)
238{
239 const float s = smoothParam->getCurrentValue();
240 newPhase = ((1.0f - s) * newPhase) + (s * AudioFadeCurve::SCurve::get (newPhase));
241
242 while (newPhase >= 1.0f) newPhase -= 1.0f;
243 while (newPhase < 0.0f) newPhase += 1.0f;
244
245 if (newPhase < getCurrentPhase())
246 {
247 previousRandom = currentRandom;
248
249 const bool bp = getBoolParamValue (*bipolarParam);
250 const float sd = stepDepthParam->getCurrentValue() * (bp ? 1.0f : 0.5f);
251 const auto randomRange = juce::Range<float> (currentRandom - sd, currentRandom + sd)
252 .getIntersectionWith ({ bp ? -1.0f : 0.0f, 1.0f });
253
254 currentRandom = juce::jmap (rand.nextFloat(), randomRange.getStart(), randomRange.getEnd());
255 jassert (randomRange.contains (currentRandom));
256 randomDifference = currentRandom - previousRandom;
257 }
258
259 jassert (juce::isPositiveAndBelow (newPhase, 1.0f));
260 currentPhase.store (newPhase, std::memory_order_release);
261
262 auto newValue = [this, newPhase, s = shapeParam->getCurrentValue()]
263 {
264 if (newPhase < (1.0f - s))
265 return previousRandom;
266
267 jassert (s > 0.0f);
268 const float skewedPhase = ((newPhase - 1.0f) / s) + 1.0f;
269 return (randomDifference * skewedPhase) + previousRandom;
270 }();
271
272 newValue *= depthParam->getCurrentValue();
273
274 currentValue.store (newValue, std::memory_order_release);
275}
276
277void RandomModifier::valueTreeChanged()
278{
279 if (! changedTimer.isTimerRunning())
280 changedTimer.startTimerHz (30);
281}
282
283}} // namespace tracktion { inline namespace engine
float nextFloat() noexcept
constexpr Range getIntersectionWith(Range other) const noexcept
int size() const noexcept
void startTimerHz(int timerFrequencyHz) noexcept
bool isTimerRunning() const noexcept
void addListener(Listener *listener)
void removeListener(Listener *listener)
void restoreChangedParametersFromState()
Restores the value of any explicitly set parameters.
void updateParameterStreams(TimePosition)
Updates all the parameter streams to their positions at this time.
const EditItemID itemID
Every EditItem has an ID which is unique within the edit.
The Tracktion Edit class!
void removeModifierTimer(ModifierTimer &)
Removes a ModifierTimer previously added.
TempoSequence tempoSequence
The global TempoSequence of this Edit.
void addModifierTimer(ModifierTimer &)
Adds a ModifierTimer to be updated each block.
float getCurrentValue() override
Returns the current value of the LFO.
void applyToBuffer(const PluginRenderContext &) override
Sub classes should implement this to process the Modifier.
AutomatableParameter::ModifierAssignment * createAssignment(const juce::ValueTree &) override
Must return a new ModifierAssignment for a given state.
float getCurrentPhase() const noexcept
Returns the current phase between 0 & 1.
void initialise() override
Call this once after construction to connect it to the audio graph.
virtual void changed()
This should be called to send a change notification to any SelectableListeners that are registered wi...
T fmod(T... args)
T is_pointer_v
#define TRANS(stringLiteral)
#define NEEDS_TRANS(stringLiteral)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
T load(T... args)
typedef float
constexpr Type jmap(Type value0To1, Type targetRangeMin, Type targetRangeMax)
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
int roundToInt(const FloatType value) noexcept
tempo::Sequence::Position createPosition(const TempoSequence &s)
Creates a Position to iterate over the given TempoSequence.
bool getBoolParamValue(const AutomatableParameter &ap)
Returns a bool version of an AutomatableParameter.
T store(T... args)
constexpr double inBeats() const
Returns the position as a number of beats.
Represents a position in real-life time.
constexpr double inSeconds() const
Returns the TimePosition as a number of seconds.
A Sequence::Position is an iterator through a Sequence.
TimeSignature getTimeSignature() const
Returns the current TimeSignature of the Position.
BeatPosition getBeats() const
Returns the current beats of the Position.
void set(TimePosition)
Sets the Position to a new time.
double getTempo() const
Returns the current tempo of the Position.
Connects a modifier source to an AutomatableParameter.
Base class for objects which need to know about the global Edit time every block.
Bass class for parameter Modifiers.
juce::ValueTree state
Modifier internal state.
void setEditTime(TimePosition newEditTime)
Subclasses can call this to update the edit time of the current value.
double getSampleRate() const
Returns the sample rate the Modifier has been initialised with.
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.
MidiMessageArray * bufferForMidiMessages
A buffer of MIDI events to process.
A ramp which goes between 0 and 1 over a set duration.