HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_dsp.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm> // fill()
4#include <memory>
5#include <ostream>
6#include <string>
7#include <unordered_map>
8
11#include "envelopes/hart_envelope.hpp"
12#include "hart_utils.hpp" // make_unique()
13
14namespace hart
15{
16
17/// @defgroup DSP DSP
18/// @brief Process signals
19
20/// @brief Hash table of automation envelope sequences mapped to param ids.
21/// @details Keys: Param IDs (int enums like GainDb::gainDb)
22/// Values: Sequence of automation envelope values for this Param ID, one value per frame
23using EnvelopeBuffers = std::unordered_map<int, std::vector<double>>;
24
25/// @brief Polymorphic base for all DSP
26/// @warning This class exists only for type erasure and polymorphism.
27/// Do NOT inherit custom DSP from this class directly.
28/// Inherit from @ref hart::DSP instead.
29/// @ingroup DSP
30template <typename SampleType>
32{
33public:
34 /// @brief Prepare for processing
35 /// @details In real-time DSP, such methods are usually used for allocating memory and other non-realtime-safe and heavyweight
36 /// operations. But keep in mind that that HART does not do real-time processing, so this merely follows common real-time
37 /// DSP design conventions, where non-realtime operations are done in a separate callback like this one.
38 /// This method is guaranteed to be called after @ref supportsChannelLayout() and @ref supportsSampleRate(), but before @ref process().
39 /// It is guaranteed that the number of input and output channels obeys supportsChannelLayout() and supportsSampleRate() preferences.
40 /// It is guaranteed that all subsequent process() calls will be in line with the arguments received in this callback.
41 /// @param sampleRateHz sample rate at which the audio should be interpreted and processed
42 /// @param numInputChannels Number of input channels
43 /// @param numOutputChannels Number of output channels
44 /// @param maxBlockSizeFrames Maximum block size in frames (samples)
45 virtual void prepare (double sampleRateHz, size_t numInputChannels, size_t numOutputChannels, size_t maxBlockSizeFrames) = 0;
46
47 /// @brief Processes the audio
48 /// @details Depending on circumstances, this callback will either be called once to process an entire piece of audio from
49 /// start to finish, or called repeatedly, one block at a time (see @ref AudioTestBuilder::withBlockSize()).
50 /// All audio blocks except the last one are guaranteed to be equal to ```maxBlockSizeFrames``` set in @ref prepare() callback.
51 /// It is guaranteed that input and output buffers are equal in length in frames (samples) to each,
52 /// but they might have different number of channels. Use @ref supportsChannelLayout() to indicate
53 /// whether the effect supports a specific i/o configuration or not, as it will be called before @ref prepare().
54 /// It is guaranteed that ```envelopeBuffers``` will only contain the values for all attached envelopes for this instance of DSP
55 /// effect, and will not contain any data (including key with empty item) if there's no envelope attached to a specific parameter
56 /// ID in this effects's instance. It will never contain envelopes for IDs that get rejected by @ref supportsEnvelopeFor().
57 /// @note This method may be called in a replacing manner, i. e. ```input``` and ```output``` may be references to the same object.
58 /// @warning Remember that the very last block of audio is almost always smaller than the block size set in @ref prepare(), so be
59 /// careful with buffer bounds.
60 /// @note Note that this method does not have to be real-time safe, as all rendering always happens offline.
61 /// Also note that, unlike real-time audio applications, this method is called on the same thread as all others like @ref prepare().
62 /// @param input Input audio block
63 /// @param output Output audio block
64 /// @param envelopeBuffers Envelope values for this block, see @ref EnvelopeBuffers
65 /// @param channelsToProcess Channels that need processing. Process channels that are marked `true`, bypass ones marked `false`.
66 virtual void process (const AudioBuffer<SampleType>& input, AudioBuffer<SampleType>& output, const EnvelopeBuffers& envelopeBuffers, ChannelFlags channelsToProcess) = 0;
67
68 /// @brief Resets to initial state
69 /// @details Ideally should be implemented in a way that audio produced after resetting is identical to audio produced after instantiation
70 virtual void reset() = 0;
71
72 /// @brief Sets DSP value
73 /// @param paramId Some ID that your subclass understands;
74 /// use of enums is encouraged for readability
75 /// @param value Value of the param in an appropriate unit;
76 /// use of SI units is encouraged (i.e. s instead of ms. Hz instead of kHz) to make better use of unit literals (see @ref Units)
77 /// @warning This method is only called to set a fixed value before processing, and is not called to do automation (via @ref hart::Envelope)
78 /// If you want your class to support automation for a specific parameter, override @ref supportsEnvelopeFor(), and then
79 /// use @c envelopeBuffers provided in @ref process() callback.
80 virtual void setValue (int paramId, double value) = 0;
81
82 /// @brief Retrieves DSP value
83 /// @details Among other things, it can be used to retrieve various readings like Gain Reduction measurements from your effect for further inspection
84 /// @param paramId Some ID that your subclass understands
85 /// @return The value of requested parameter in a unit that your subclass understands
86 /// @note This method is only intended for parameters that don't have an automation envelope attached to this specific instance.
87 /// To get values for automated parameters, use @c envelopeBuffers provided in @ref process() callback.
88 virtual double getValue (int paramId) const = 0;
89
90 /// @brief Tells the runner (host) whether this effect supports a specific i/o configuration.
91 /// @details It is guaranteed that the effect will not receive unsupported number of channels in @ref process().
92 /// However, it is not always possible to handle gracefully channel layout being unsupported, so in some circumstances
93 /// it can cause an exception or a test failure. This method is guaranteed to be called at least once before @ref prepare()
94 virtual bool supportsChannelLayout (size_t numInputChannels, size_t numOutputChannels) const = 0;
95
96 /// @brief Makes a text representation of this DSP effect for test failure outputs.
97 /// @details It is strongly encouraged to follow python's
98 /// <a href="https://docs.python.org/3/reference/datamodel.html#object.__repr__" target="_blank">repr()</a>
99 /// conventions for returned text - basically, put something like "MyClass(value1, value2)" (with no quotes)
100 /// into the stream whenever possible, or "<Readable info in angled brackets>" otherwise.
101 /// Also, use built-in stream manipulators like @ref dbPrecision wherever applicable.
102 /// Use @ref HART_DEFINE_GENERIC_REPRESENT() to get a basic implementation for this method.
103 /// @param[out] stream Output stream to write to
104 virtual void represent (std::ostream& stream) const = 0;
105
106 /// @brief Tells whether this effect accepts automation envelopes for a particular parameter
107 /// @param paramId Some ID that your subclass understands
108 /// @return true if your subclass can process automation for this parameter, false otherwise
109 virtual bool supportsEnvelopeFor (int /* paramId */) const { return false; }
110
111 /// @brief Tells whether this effect supports given sample rate
112 /// @details It is guaranteed to be called before @ref prepare()
113 /// @param sampleRateHz Sample rate in question
114 /// @return true if effect is capable of interpreting and processing in a given sample rate, false otherwise
115 virtual bool supportsSampleRate (double /* sampleRateHz */) const { return true; }
116
117 /// @brief Returns a smart pointer with a copy of this object
118 virtual std::unique_ptr<DSPBase<SampleType>> copy() const = 0;
119
120 /// @brief Returns a smart pointer with a moved instance of this object
121 virtual std::unique_ptr<DSPBase<SampleType>> move() = 0;
122
123 /// @brief Destructor
124 virtual ~DSPBase() = default;
125
126 /// @brief Default constructor
127 DSPBase() = default;
128
129 /// @brief Copies from another DSP effect instance
130 /// @details Attached automation envelopes are deep-copied
131 DSPBase (const DSPBase& other):
132 m_channelsToProcess (other.m_channelsToProcess)
133 {
134 for (auto& pair : other.m_envelopes)
135 m_envelopes.emplace (pair.first, pair.second->copy());
136 }
137
138 /// @brief Move constructor
139 /// @details Attached automation envelopes are moved as well
140 DSPBase (DSPBase&& other) noexcept:
141 m_envelopes (std::move (other.m_envelopes)),
142 m_channelsToProcess (other.m_channelsToProcess)
143 {
144 }
145
146 /// @brief Copies from another DSP effect instance
147 /// @details Attached automation envelopes are deep-copied
148 DSPBase& operator= (const DSPBase& other)
149 {
150 if (this == &other)
151 return *this;
152
153 for (auto& pair : other.m_envelopes)
154 m_envelopes.emplace (pair.first, pair.second->copy());
155
156 m_channelsToProcess = other.m_channelsToProcess;
157 return *this;
158 }
159
160 /// @brief Move assignment
161 /// @details Attached automation envelopes are moved as well
162 DSPBase& operator= (DSPBase&& other) noexcept
163 {
164 if (this != &other)
165 m_envelopes = std::move (other.m_envelopes);
166
167 m_channelsToProcess = other.m_channelsToProcess;
168 return *this;
169 }
170
171 /// @brief Checks if there's an automation envelope attached to a specific parameter
172 /// @details The envelopes are guaranteed to be attached strictly before @ref prepare()
173 /// callback, so by the time of the first @ref process() call consider the presence or
174 /// absence of envelope permanent.
175 /// @return Reference to itself for chaining
176 bool hasEnvelopeFor (int paramId)
177 {
178 return m_envelopes.find (paramId) != m_envelopes.end();
179 }
180
181 /// @brief Prepares all the attached envelopes and the effect itself for processing
182 /// @details This method is intended to be called by DSP hosts like @ref hart::AudioTestBuilder or @ref hart::Signal.
183 /// If you're making something that owns an instance of a DSP and needs it to generate audio,
184 /// you must call this method before calling @ref processWithEnvelopes().
185 /// You must also call @ref supportsChannelLayout() and @ref supportsSampleRate() before calling this method.
186 /// @attention If you're not making a custom host, you probably don't need to call this method.
187 void prepareWithEnvelopes (double sampleRateHz, size_t numInputChannels, size_t numOutputChannels, size_t maxBlockSizeFrames)
188 {
189 m_envelopeBuffers.clear(); // TODO: Remove only unused buffers
190
191 for (auto& item : m_envelopes)
192 {
193 const int paramId = item.first;
194 m_envelopeBuffers.emplace (paramId, std::vector<double> (maxBlockSizeFrames));
195 }
196
197 hassert (m_envelopes.size() == m_envelopeBuffers.size());
198
199 for (auto& item : m_envelopeBuffers)
200 {
201 const int paramId = item.first;
202 auto& envelopeBuffer = item.second;
203
204 // Sanity checks
205 hassert (supportsEnvelopeFor (paramId) && "Envelope for this id is unsupported, yet there's an envelope buffer allocated for it");
206 hassert (hasEnvelopeFor (paramId) && "Envelope for this param id is not attached, yet there's an envelope buffer allocated for it");
207
208 if (envelopeBuffer.size() != maxBlockSizeFrames)
209 envelopeBuffer.resize (maxBlockSizeFrames);
210 }
211
212 m_channelsToProcess.resize (numInputChannels);
213 prepare (sampleRateHz, numInputChannels, numOutputChannels, maxBlockSizeFrames);
214 }
215
216 /// @brief Returns a structure indicating which channels should be processed by this DSP
217 /// @details See @ref atChannels(), @ref atChannel(), @ref atAllChannels()
218 /// @return Set of flags for each channel, see @ref hart::ChannelFlags.
219 /// `true` for channels that need processing, `false` for channels that need bypassing.
221 {
222 return m_channelsToProcess;
223 }
224
225 /// @brief Renders all the automation envelopes and processes the audio
226 /// @details This method is intended to be called by DSP hosts like @ref hart::AudioTestBuilder @ref hart::Signal.
227 /// If you're making something that owns an instance of a Signal and needs it to generate audio,
228 /// you must call it after calling @ref prepareWithEnvelopes().
229 /// @attention If you're not making a custom host, you probably don't need to call this method.
230 /// @param input Input audio block
231 /// @param output Output audio block
232 void processWithEnvelopes (const AudioBuffer<SampleType>& input, AudioBuffer<SampleType>& output)
233 {
234 for (auto& item : m_envelopeBuffers)
235 {
236 const int paramId = item.first;
237 auto& envelopeBuffer = item.second;
238
239 // Sanity checks
240 hassert (supportsEnvelopeFor (paramId) && "Envelope for this id is unsupported, yet there's an envelope buffer allocated for it");
241 hassert (hasEnvelopeFor (paramId) && "Envelope for this param id is not attached, yet there's an envelope buffer allocated for it");
242 hassert (input.getNumFrames() <= envelopeBuffer.size() && "Envelope Buffers were not allocated properly for this buffer size");
243
244 // Render envelope values
245 getValues (paramId, envelopeBuffer.size(), envelopeBuffer);
246 }
247
248 process (input, output, m_envelopeBuffers, m_channelsToProcess);
249 }
250
251 /// @brief Makes a text representation of this DSP with optional "atChannels" appendix
252 /// @details For internal use by hosts
253 void representWithActiveChannels (std::ostream& stream) const
254 {
255 this->represent (stream);
256
258 return;
259
260 stream << ".atChannels (";
262 stream << ')';
263 }
264
265 /// @brief Helper for template resolution
266 /// @private
267 using SampleTypePublicAlias = SampleType;
268
269protected:
270 std::unordered_map<int, std::unique_ptr<Envelope>> m_envelopes;
272
273private:
274 EnvelopeBuffers m_envelopeBuffers;
275
276 /// @brief Gets sample-accurate automation envelope values for a specific parameter
277 /// @param[in] paramId Some ID that your subclass understands
278 /// @param[in] blockSize Buffer size in frames, should be the same as ```input```/```output```'s size in @ref process()
279 /// @param[out] valuesOutput Container to get filled with the rendered automation values
280 void getValues (int paramId, size_t blockSize, std::vector<double>& valuesOutput)
281 {
282 if (valuesOutput.size() < blockSize)
283 {
284 HART_WARNING ("Make sure to configure your envelope container size before processing audio");
285 valuesOutput.resize (blockSize);
286 }
287
288 if (! hasEnvelopeFor (paramId))
289 {
290 const double value = getValue (paramId);
291 std::fill (valuesOutput.begin(), valuesOutput.end(), value);
292 }
293 else
294 {
295 m_envelopes[paramId]->renderNextBlock (blockSize, valuesOutput);
296 }
297 }
298};
299
300/// @brief Base for DSP effects
301/// @details This class is used both for adapting your DSP classes that you wish to test,
302/// and for using in DSP chains of @ref Signals, so you can use stock effects like @ref GainDb
303/// as tested processors, and you can use your tested DSP subclasses in Signals' DSP chains with
304/// other effects. You can even chain multiple of your own DSP classes together this way.
305/// All the callbacks of this class are guaranteed to be called from the same thread.
306/// To make your custom DSP wrapper, inherit from it like so
307/// (note the <a href="https://en.cppreference.com/w/cpp/language/crtp.html">CRTP</a>
308/// in the template args here, it's important!):
309/// @code{.cpp}
310/// template<typename SampleType>
311/// class MyCustomDSP: public DSP<SampleType, MyCustomDSP<SampleType>>
312/// {
313/// // ...
314/// };
315/// @endcode
316/// @tparam SampleType Type of values that will be processed, typically ```float``` or ```double```
317/// @tparam Derived Subclass for CRTP
318/// @ingroup DSP
319template<typename SampleType, typename Derived>
320class DSP:
321 public DSPBase<SampleType>
322{
323public:
324
325 /// @brief Adds envelope to a specific parameter by moving it
326 /// @details Guaranteed to be called strictly after the @ref supportsEnvelopeFor() callback,
327 /// and only if it has returned ```true``` for this specific ```paramId```.
328 /// Can be chained together like ```myEffect.withEnvelope (someId, someEnvelope).withEnvelope (otherId, otherEnvelope)```.
329 /// If called multiple times for the same paramId, only last envelope for this ID will be used, all previous ones will be descarded.
330 /// @param paramId Some ID that your subclass understands
331 /// @param envelope Envelope to be attached
332 /// @return Reference to itself for chaining
333 Derived& withEnvelope (int paramId, Envelope&& envelope)
334 {
335 if (! this->supportsEnvelopeFor (paramId))
336 HART_THROW_OR_RETURN (hart::UnsupportedError, std::string ("DSP doesn't support envelopes for param ID: ") + std::to_string (paramId), *this);
337
338 this->m_envelopes.emplace (paramId, hart::make_unique<Envelope> (std::move (envelope)));
339 return static_cast<Derived&> (*this);
340 }
341
342 /// @brief Adds envelope to a specific parameter by copying it
343 /// @details Guaranteed to be called strictly after the @ref supportsEnvelopeFor() callback,
344 /// and only if it has returned ```true``` for this specific ```paramId```.
345 /// Can be chained together like ```myEffect.withEnvelope (someId, someEnvelope).withEnvelope (otherId, otherEnvelope)```.
346 /// If called multiple times for the same paramId, only last envelope for this ID will be used, all previous ones will be descarded.
347 /// @param paramId Some ID that your subclass understands
348 /// @param envelope Envelope to be attached
349 /// @return Reference to itself for chaining
350 Derived& withEnvelope (int paramId, const Envelope& envelope)
351 {
352 if (! this->supportsEnvelopeFor(paramId))
353 HART_THROW_OR_RETURN (hart::UnsupportedError, std::string ("DSP doesn't support envelopes for param ID: ") + std::to_string (paramId), *this);
354
355 this->m_envelopes.emplace (paramId, envelope.copy());
356 return static_cast<Derived&> (*this);
357 }
358
359 // TODO: withEnvelope() that takes a unique_ptr to the envelope
360
361 /// @brief Returns a smart pointer with a copy of this object
362 virtual std::unique_ptr<DSPBase<SampleType>> copy() const override
363 {
364 return hart::make_unique<Derived> (static_cast<const Derived&> (*this));
365 }
366
367 /// @brief Returns a smart pointer with a moved instance of this object
368 virtual std::unique_ptr<DSPBase<SampleType>> move() override
369 {
370 return hart::make_unique<Derived> (std::move (static_cast<Derived&> (*this)));
371 }
372
373 /// @brief Makes this DSP process only specific channels, and ignore the rest
374 /// @details If not set, the DSP applies to all channels by default.
375 /// If you call this method multiple times, only the last one will be applied.
376 /// To select only one channel, consider using @ref DSP::atChannel() instead.
377 /// @param channelsToProcess List of channels this DSP should apply to,
378 /// e.g. `{0, 1}` or `{Channel::left, Channel::right}` for left and right channels only.
379 /// @see `hart::Channel`
380 Derived& atChannels (std::initializer_list<size_t> channelsToProcess)
381 {
382 this->m_channelsToProcess.setAllTo (false);
383
384 for (size_t channel : channelsToProcess)
385 {
386 if (channel >= this->m_channelsToProcess.size())
387 HART_THROW_OR_RETURN_VOID (hart::ValueError, "Channel exceeds max number of channels");
388
389 this->m_channelsToProcess[channel] = true;
390 }
391
392 return static_cast<Derived&> (*this);
393 }
394
395 /// @brief Makes this DSP process only specific channels, and bypass the rest
396 /// @details If not set, the DSP applies to all channels by default.
397 /// If you call this method multiple times, only the last one will be applied.
398 /// To select multiple channels, use @ref DSP::atChannels() or
399 /// @ref atAllChannelsExcept() instead.
400 /// @param channelToProcess Channel this DSP should apply to (zero-based),
401 /// e.g. `0` or `Channel::left` for left channel.
402 /// @note If not set, the DSP applies to all channels by default
403 /// @see `hart::Channel`
404 Derived& atChannel (size_t channelToProcess)
405 {
406 if (channelToProcess >= this->m_channelsToProcess.size())
407 HART_THROW_OR_RETURN_VOID (hart::ValueError, "Channel exceeds max number of channels");
408
409 this->m_channelsToProcess.setAllTo (false);
410 this->m_channelsToProcess[channelToProcess] = true;
411
412 return static_cast<Derived&> (*this);
413 }
414
415 /// @brief Makes this DSP apply to all channels
416 /// @details This is the default setting anyway, so this method is only
417 /// for cases when you need to override previous @ref atChannel(),
418 /// @ref atChannels() or @ref atAllChannelsExcept() calls.
419 Derived& atAllChannels()
420 {
421 this->m_channelsToProcess.setAllTo (true);
422 return static_cast<Derived&> (*this);
423 }
424
425 /// @brief Makes this DSP process only specific channels, and bypass the rest
426 /// @details If not set, DSP applies to all channels by default.
427 /// If you call this method multiple times, only the last one will be applied.
428 /// @param channelsToSkip List of channels this DSP should NOT apply to,
429 /// e.g. `{0, 1}` or `{Channel::left, Channel::right}` to bypass left and right
430 /// channels, and process the rest
431 /// @see `hart::Channel`
432 Derived& atAllChannelsExcept (std::initializer_list<size_t> channelsToSkip)
433 {
434 this->m_channelsToProcess.setAllTo (true);
435
436 for (size_t channel : channelsToSkip)
437 {
438 if (channel >= this->m_channelsToProcess.size())
439 HART_THROW_OR_RETURN_VOID (hart::ValueError, "Channel exceeds max number of channels");
440
441 this->m_channelsToProcess[channel] = false;
442 }
443
444 return static_cast<Derived&> (*this);
445 }
446};
447
448/// @brief Prints readable text representation of the DSP object into the I/O stream
449/// @relates DSP
450/// @ingroup DSP
451template <typename SampleType>
452inline std::ostream& operator<< (std::ostream& stream, const DSPBase<SampleType>& dsp)
453{
454 // TODO: Represent with the envelopes
455 dsp.representWithActiveChannels (stream);
456 return stream;
457}
458
459/// @brief Forbids @ref hart::DSP::copy() and @ref hart::DSP::move() methods
460/// @details Put this into your class body's ```public``` section if either is true:
461/// - Your class is not trivially copyable and movable
462/// - You don't want to trouble yourself with implementing move and copy semantics for your class
463///
464/// Otherwise, use @ref HART_DSP_DEFINE_COPY_AND_MOVE() instead.
465/// Obviously, you won't be able to pass your class to the host
466/// by reference, copy or explicit move, but you still can pass
467/// it wrapped into a smart pointer like so:
468/// ```cpp
469/// processAudioWith (hart::make_unique<MyDspType>()).withThis().withThat().process();
470/// ```
471/// But it's still better to get your move and copy semantics figured out - this is a
472/// perfect chance to stress-test your effect's resource management, among other things!
473/// @ingroup DSP
474#define HART_DSP_FORBID_COPY_AND_MOVE
475 std::unique_ptr<DSP<SampleType>> copy() const override {
476 static_assert(false, "This DSP cannot be copied");
477 return nullptr;
478 }
479 std::unique_ptr<DSP<SampleType>> move() override {
480 static_assert(false, "This DSP cannot be moved");
481 return nullptr;
482 }
483
484} // namespace hart
485
486/// @private
487#define HART_DSP_DECLARE_ALIASES_FOR(ClassName)
488 namespace aliases_float{
489 using ClassName = hart::ClassName<float>;
490 }
491 namespace aliases_double{
492 using ClassName = hart::ClassName<double>;
493 }
A set of boolean flags mapped to each audio channel.
bool allTrue() const noexcept
Checks if all flags are set to true
ChannelFlags(bool defaultValues=true, size_t numChannels=m_maxChannels)
Creates a new channel flags object.
void resize(size_t newNumChannels)
Resizes the container.
void representAsInitializerList(std::ostream &stream) const
Makes text representation of itself as a initializer list of active channels.
Polymorphic base for all DSP.
Definition hart_dsp.hpp:32
virtual ~DSPBase()=default
Destructor.
DSPBase(const DSPBase &other)
Copies from another DSP effect instance.
Definition hart_dsp.hpp:131
virtual bool supportsChannelLayout(size_t numInputChannels, size_t numOutputChannels) const =0
Tells the runner (host) whether this effect supports a specific i/o configuration.
void prepareWithEnvelopes(double sampleRateHz, size_t numInputChannels, size_t numOutputChannels, size_t maxBlockSizeFrames)
Prepares all the attached envelopes and the effect itself for processing.
Definition hart_dsp.hpp:187
void representWithActiveChannels(std::ostream &stream) const
Makes a text representation of this DSP with optional "atChannels" appendix.
Definition hart_dsp.hpp:253
virtual void reset()=0
Resets to initial state.
virtual std::unique_ptr< DSPBase< SampleType > > move()=0
Returns a smart pointer with a moved instance of this object.
virtual void represent(std::ostream &stream) const =0
Makes a text representation of this DSP effect for test failure outputs.
virtual std::unique_ptr< DSPBase< SampleType > > copy() const =0
Returns a smart pointer with a copy of this object.
virtual double getValue(int paramId) const =0
Retrieves DSP value.
ChannelFlags m_channelsToProcess
Definition hart_dsp.hpp:271
DSPBase(DSPBase &&other) noexcept
Move constructor.
Definition hart_dsp.hpp:140
virtual bool supportsSampleRate(double) const
Tells whether this effect supports given sample rate.
Definition hart_dsp.hpp:115
void processWithEnvelopes(const AudioBuffer< SampleType > &input, AudioBuffer< SampleType > &output)
Renders all the automation envelopes and processes the audio.
Definition hart_dsp.hpp:232
DSPBase()=default
Default constructor.
virtual void prepare(double sampleRateHz, size_t numInputChannels, size_t numOutputChannels, size_t maxBlockSizeFrames)=0
Prepare for processing.
virtual void setValue(int paramId, double value)=0
Sets DSP value.
DSPBase & operator=(const DSPBase &other)
Copies from another DSP effect instance.
Definition hart_dsp.hpp:148
bool hasEnvelopeFor(int paramId)
Checks if there's an automation envelope attached to a specific parameter.
Definition hart_dsp.hpp:176
virtual void process(const AudioBuffer< SampleType > &input, AudioBuffer< SampleType > &output, const EnvelopeBuffers &envelopeBuffers, ChannelFlags channelsToProcess)=0
Processes the audio.
ChannelFlags getChannelsToProcess()
Returns a structure indicating which channels should be processed by this DSP.
Definition hart_dsp.hpp:220
DSPBase & operator=(DSPBase &&other) noexcept
Move assignment.
Definition hart_dsp.hpp:162
virtual bool supportsEnvelopeFor(int) const
Tells whether this effect accepts automation envelopes for a particular parameter.
Definition hart_dsp.hpp:109
std::unordered_map< int, std::unique_ptr< Envelope > > m_envelopes
Definition hart_dsp.hpp:270
Base for DSP effects.
Definition hart_dsp.hpp:322
Derived & atChannels(std::initializer_list< size_t > channelsToProcess)
Makes this DSP process only specific channels, and ignore the rest.
Definition hart_dsp.hpp:380
virtual std::unique_ptr< DSPBase< SampleType > > copy() const override
Returns a smart pointer with a copy of this object.
Definition hart_dsp.hpp:362
Derived & atAllChannelsExcept(std::initializer_list< size_t > channelsToSkip)
Makes this DSP process only specific channels, and bypass the rest.
Definition hart_dsp.hpp:432
virtual std::unique_ptr< DSPBase< SampleType > > move() override
Returns a smart pointer with a moved instance of this object.
Definition hart_dsp.hpp:368
Derived & withEnvelope(int paramId, Envelope &&envelope)
Adds envelope to a specific parameter by moving it.
Definition hart_dsp.hpp:333
Derived & atChannel(size_t channelToProcess)
Makes this DSP process only specific channels, and bypass the rest.
Definition hart_dsp.hpp:404
Derived & atAllChannels()
Makes this DSP apply to all channels.
Definition hart_dsp.hpp:419
Derived & withEnvelope(int paramId, const Envelope &envelope)
Adds envelope to a specific parameter by copying it.
Definition hart_dsp.hpp:350
Represents an Envelope curve for DSP parameters.
virtual void renderNextBlock(size_t blockSize, std::vector< double > &valuesOutput)=0
virtual std::unique_ptr< Envelope > copy() const =0
std::unique_ptr< ObjectType > make_unique(Args &&... args)
std::make_unique() replacement for C++11
#define HART_THROW_OR_RETURN_VOID(ExceptionType, message)
#define HART_WARNING(message)
#define hassert(condition)
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)