Krotos Modules 3
Loading...
Searching...
No Matches
KAttachment.cpp
Go to the documentation of this file.
1namespace krotos
2{
3 KAttachment::KAttachment(std::shared_ptr<KParameter> paramToAttach, std::function<void(float)> updateFunction)
4 : m_parameterPtr(paramToAttach), m_parameterValue(paramToAttach->getValue()), m_attachmentMode(Mode::Discrete),
5 m_updater(nullptr), m_smoothingTimeSeconds(0.0f), m_updateFunction(std::move(updateFunction))
6 {
7 jassert(paramToAttach != nullptr);
8
9 if (auto parameter = m_parameterPtr.lock())
10 {
11 parameter->addListener(this);
12 m_cachedParameterPtr = parameter.get();
13 }
14 }
15
16 KAttachment::KAttachment(std::shared_ptr<KParameter> paramToAttach, float smoothingTimeSeconds, int updateRate,
17 std::function<void(float)> updateFunction)
18 : m_parameterPtr(paramToAttach), m_parameterValue(paramToAttach->getValue()),
19 m_attachmentMode(Mode::Continuous), m_updater(new ParameterUpdater(*this)),
20 m_smoothingTimeSeconds(smoothingTimeSeconds), m_updateFunction(std::move(updateFunction)),
21 m_updateRate(updateRate)
22 {
23 jassert(paramToAttach != nullptr);
24
25 if (smoothingTimeSeconds <= 0.0f)
26 {
27 // Continuous parameter attachments must satisfy the above condition.
28 // If you want a discrete parameter, use the discrete constructor.
29 jassertfalse;
31 }
32
33 if (updateFunction != nullptr && updateRate <= 0)
34 {
35 // If you are using the KAttachment's updater, then you must use a valid update rate.
36 jassertfalse;
37 updateRate = 1;
38 }
39 if (auto parameter = m_parameterPtr.lock())
40 {
41 parameter->addListener(this);
42 }
43 }
44
46 {
47 if (auto parameter = m_parameterPtr.lock())
48 {
49 parameter->removeListener(this);
50 }
51 }
52
53 void KAttachment::prepare(double sampleRate, int samplesPerBlock)
54 {
55 m_sampleRate = sampleRate;
56 m_maxBlockSize = samplesPerBlock;
57 m_valueBuffer.setSize(1, m_maxBlockSize);
58
59 auto numSmoothingSamples = static_cast<int>(m_sampleRate * m_smoothingTimeSeconds);
60
61 // if no smoothing is desired, the smoother is set to 1 sample
62 if (numSmoothingSamples == 0)
64 else
65 m_numSmoothingSamples = jmax<int>(numSmoothingSamples, samplesPerBlock);
66
68 if (auto parameter = m_parameterPtr.lock())
69 {
70 parameterValueChanged(0, parameter->getValue());
71 m_cachedParameterPtr = parameter.get();
72 }
74 }
75
76 bool KAttachment::nextBlock(int numSamples)
77 {
78 // You must call prepare() first!
79 jassert(m_sampleRate > 0.0);
80
81 if (m_attachmentMode == Mode::Discrete)
82 {
83 if (m_smoother.isSmoothing())
84 {
85 // Update the parameter buffer
86 auto bufferPtr = m_valueBuffer.getWritePointer(0);
87 auto newValue = m_cachedParameterPtr->convertFrom0to1(m_smoother.getNextValue());
88 for (int i = 0; i < numSamples; i++)
89 bufferPtr[i] = newValue;
90
91 // Update the audio processor
92 if (m_updateFunction != nullptr)
93 m_updateFunction(newValue);
94 }
95
96 return false;
97 }
98 else
99 {
100 // Once the smoother is done smoothing, the parameter value buffer is updated for
101 // one more block to ensure that all changes are flushed to the processor.
102 if (!m_fastModulators.isEmpty() || m_smoother.isSmoothing())
103 {
104 m_wasSmoothing = true;
105 m_needsUpdate = true;
106 }
107 else if (!m_smoother.isSmoothing() && m_wasSmoothing)
108 {
109 m_wasSmoothing = false;
110 m_needsUpdate = true;
111 }
112 else
113 {
114 m_needsUpdate = false;
115 return false;
116 }
117
118 // Fill buffer with output of smoother
119 auto bufferPtr = m_valueBuffer.getWritePointer(0);
120 for (int i = 0; i < numSamples; i++)
121 bufferPtr[i] = m_smoother.getNextValue();
122
123 // Add modulation output to the buffer
124 for (auto modulator : m_fastModulators)
125 {
126 auto d = m_depthMap.at(modulator);
127 auto depth = d.value;
128 bool isBipolar = d.polarity == Polarity::Bipolar;
129 auto modValues = modulator->getValues();
130
131 for (int i = 0; i < numSamples; i++)
132 {
133 // Calculate the modulation value and add it to the parameter value.
134 // For bipolar modulation, rescale the modulation value from [0.0, 1.0] to [-1.0, 1.0]
135 // and halve the depth factor.
136 bufferPtr[i] += isBipolar ? ((modValues[i] * 2.0f) - 1.0f) * (depth * 0.5f) : modValues[i] * depth;
137 }
138 }
139
140 // Denormalize the buffer to get the actual parameter values
141 for (int i = 0; i < numSamples; i++)
142 bufferPtr[i] = m_cachedParameterPtr->convertFrom0to1(bufferPtr[i]);
143
144 // Reset the ParameterUpdater
145 if (needsUpdate())
146 {
147 m_updater->reset();
148 return true;
149 }
150
151 return false;
152 }
153 }
154
155 const float* KAttachment::data() { return m_valueBuffer.getReadPointer(0); }
156
158 {
159 // You have to check for an active updater before calling this function,
160 // either with needsUpdate() or the return value of nextBlock()
161 jassert(needsUpdate());
162
163 return m_updater.get();
164 }
165
166 void KAttachment::addModulator(Modulator* m, float depth, Polarity polarity)
167 {
168 if (m->isFast())
169 {
170 m_fastModulators.add(m);
171 m->addListener(this);
172 }
173 else
174 {
175 m_slowModulators.add(m);
176 m->addListener(this);
177 }
178
179 struct Depth d = {depth, polarity};
180 m_depthMap.emplace(m, d);
181 }
182
184 {
185 m_fastModulators.removeFirstMatchingValue(m);
186 m_slowModulators.removeFirstMatchingValue(m);
187 m->removeListener(this);
188
189 m_depthMap.erase(m);
190
191 // reset parameter to value without the modulation
193 }
194
196 {
197 jassert(m_depthMap.count(m) == 1);
198 jassert(depth >= -1.0f && depth <= 1.0f);
199
200 m_depthMap[m].value = depth;
202 }
203
205 {
206 jassert(m_depthMap.count(m) == 1);
207
208 m_depthMap[m].polarity = polarity;
210 }
211
213 {
214 if (m_smoother.isSmoothing())
216 }
217
218 bool KAttachment::needsUpdate() const { return m_needsUpdate && m_updateFunction != nullptr; }
219
220 void KAttachment::parameterValueChanged(int /*paramIndex*/, float newValue)
221 {
222 m_parameterValue = newValue;
224 }
225
227 {
228 // Initialize the smoother with the current parameter value
229 auto currentVal = m_smoother.getTargetValue();
230 m_smoother.setCurrentAndTargetValue(currentVal);
231
232 // Update all the values in the buffer to match the current parameter value
233 auto val = m_cachedParameterPtr->convertFrom0to1(currentVal);
234 auto bufferPtr = m_valueBuffer.getWritePointer(0);
235 for (int i = 0; i < m_valueBuffer.getNumSamples(); i++)
236 bufferPtr[i] = val;
237
238 // Update the audio processor
239 if (m_updateFunction != nullptr)
240 m_updateFunction(val);
241 }
242
244 {
245 if (!m->isFast())
247 }
248
250 {
251 float value = m_parameterValue;
252
253 for (auto& modulator : m_slowModulators)
254 {
255 // if the modulator has a mapping function, the mapping function determines the output - No polarity.
256 if (modulator->mappingFunctionPtr != nullptr)
257 {
258 // Pointer to the vector of parameter values this modulator is connected to eg. XY
259 auto incomingParamValues = modulator->getValues();
260 auto depth = m_depthMap.at(modulator).value;
261
262 //check for special case of gain parameter
263 if (m_cachedParameterPtr->getNormalisableRange().getRange().getStart() <= -60.0f)
264 {
265 // Convert dB space into gain space
266 auto parameterValuedB = m_cachedParameterPtr->convertFrom0to1(value);
267 auto parameterValueGain = Decibels::decibelsToGain(
268 parameterValuedB, m_cachedParameterPtr->getNormalisableRange().getRange().getStart());
269
270 // We are now in gain space, so do the scaling
271 parameterValueGain *= modulator->mappingFunctionPtr(incomingParamValues, depth);
272
273 // Convert result from gain space back into dB space
274 parameterValuedB = Decibels::gainToDecibels(
275 parameterValueGain, m_cachedParameterPtr->getNormalisableRange().getRange().getStart());
276 value = m_cachedParameterPtr->convertTo0to1(parameterValuedB);
277 }
278 else
279 {
280 value *= modulator->mappingFunctionPtr(incomingParamValues, depth);
281 }
282 }
283 else // modulator has no mapping function
284 {
285 auto d = m_depthMap.at(modulator);
286 bool isBipolar = d.polarity == Polarity::Bipolar;
287 auto depth = d.value;
288
289 // Calculate the modulation value and add it to the parameter value.
290 // For bipolar modulation, rescale the modulation value from [0.0, 1.0] to [-1.0, 1.0]
291 // and halve the depth factor.
292 auto valueIncoming = modulator->getValues()[0];
293 auto modulationValue =
294 isBipolar ? ((valueIncoming * 2.0f) - 1.0f) * (depth * 0.5f) : valueIncoming * depth;
295 value += modulationValue;
296 }
297 }
298
299 m_smoother.setTargetValue(value);
300 }
301} // namespace krotos
Definition KAttachment.h:23
int m_numSmoothingSamples
Definition KAttachment.h:174
std::unique_ptr< ParameterUpdater > m_updater
Definition KAttachment.h:170
void sendInitialUpdate()
Definition KAttachment.cpp:226
bool m_wasSmoothing
Definition KAttachment.h:191
const float * data()
Definition KAttachment.cpp:155
void updateSmoother()
Definition KAttachment.cpp:249
void applyPendingUpdates()
Definition KAttachment.cpp:212
SmoothedValue< float > m_smoother
Definition KAttachment.h:172
void setModulatorDepth(Modulator *m, float depth)
Definition KAttachment.cpp:195
void modulatorChanged(const Modulator *m) override
Definition KAttachment.cpp:243
Array< Modulator * > m_fastModulators
Definition KAttachment.h:177
Polarity
Definition KAttachment.h:120
@ Bipolar
Definition KAttachment.h:121
AudioBuffer< float > m_valueBuffer
Definition KAttachment.h:168
bool m_needsUpdate
Definition KAttachment.h:190
void addModulator(Modulator *m, float depth=1.0f, Polarity polarity=Polarity::Bipolar)
Definition KAttachment.cpp:166
std::map< Modulator *, Depth > m_depthMap
Definition KAttachment.h:185
Array< Modulator * > m_slowModulators
Definition KAttachment.h:176
void prepare(double sampleRate, int samplesPerBlock)
Definition KAttachment.cpp:53
void setModulatorPolarity(Modulator *m, Polarity polarity)
Definition KAttachment.cpp:204
bool nextBlock(int numSamples)
Definition KAttachment.cpp:76
double m_sampleRate
Definition KAttachment.h:187
RangedAudioParameter * m_cachedParameterPtr
Definition KAttachment.h:166
Mode
Definition KAttachment.h:154
const std::function< void(float)> m_updateFunction
Definition KAttachment.h:192
KAttachment(std::shared_ptr< KParameter > paramToAttach, std::function< void(float)> updateFunction=nullptr)
Definition KAttachment.cpp:3
const Mode m_attachmentMode
Definition KAttachment.h:169
int m_maxBlockSize
Definition KAttachment.h:188
float m_smoothingTimeSeconds
Definition KAttachment.h:173
void parameterValueChanged(int, float newValue) override
Definition KAttachment.cpp:220
bool needsUpdate() const
Definition KAttachment.cpp:218
~KAttachment() override
Definition KAttachment.cpp:45
void removeModulator(Modulator *m)
Definition KAttachment.cpp:183
float m_parameterValue
Definition KAttachment.h:167
std::weak_ptr< RangedAudioParameter > const m_parameterPtr
Definition KAttachment.h:165
ParameterUpdater * getUpdater()
Definition KAttachment.cpp:157
Holds a list of modulation destinations and sends events to them when the modulator output is updated...
Definition ModulationSource.h:20
void removeListener(Listener *l)
Definition ModulationSource.cpp:7
bool isFast() const
Definition ModulationSource.h:40
void addListener(Listener *l)
Definition ModulationSource.cpp:5
Definition AirAbsorptionFilter.cpp:2
Definition KAttachment.h:180