DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
Loading...
Searching...
No Matches
NoiseGenerator.h
Go to the documentation of this file.
1// DSPark - Professional Audio DSP Framework
2// Copyright (c) 2026 Cristian Moresi - MIT License
3
4#pragma once
5
35#include "../Core/AnalogRandom.h"
36#include "../Core/AudioBuffer.h"
37#include "../Core/AudioSpec.h"
38#include "../Core/DspMath.h"
39#include "../Core/StateBlob.h"
40
41#include <algorithm>
42#include <atomic>
43#include <cmath>
44#include <cstddef>
45#include <cstdint>
46#include <utility>
47#include <vector>
48
49namespace dspark {
50
61template <FloatType T>
63{
64public:
65 enum class Type
66 {
67 White,
68 Pink,
69 Brown
70 };
71
80 void prepare(const AudioSpec& spec)
81 {
82 if (!spec.isValid()) return; // release-safe: keep previous state
83
84 sampleRate_ = spec.sampleRate;
85 int targetChannels = spec.numChannels;
86
87 generators_.clear();
88 generators_.reserve(static_cast<size_t>(targetChannels));
89
90 for (int ch = 0; ch < targetChannels; ++ch)
91 {
93 gen.prepare(sampleRate_);
94 gen.setRange(T(-1), T(1));
95 // A new value EVERY sample (rate == fs): full-bandwidth noise. A lower
96 // rate would sample-and-hold the source, giving a non-flat/imaged
97 // spectrum instead of true white/pink/brown.
98 gen.setRateHz(static_cast<T>(sampleRate_));
99 applyNoiseType(gen);
100
101 // Decorrelate output across channels
102 gen.reseed(static_cast<uint64_t>(ch + 1) * 0x9E3779B97F4A7C15ULL);
103 generators_.push_back(std::move(gen));
104 }
105
106 // Sync smoothing state to prevent ramp-up from zero on first block
107 currentGain_ = gain_.load(std::memory_order_relaxed);
108 }
109
118 void processBlock(AudioBufferView<T> buffer) noexcept
119 {
120 if (generators_.empty())
121 return;
122
123 if (typeDirty_.exchange(false, std::memory_order_acquire))
124 {
125 for (auto& gen : generators_)
126 applyNoiseType(gen);
127 }
128
129 const int numCh = std::min<int>(buffer.getNumChannels(), static_cast<int>(generators_.size()));
130 const int numSamples = buffer.getNumSamples();
131
132 if (numSamples <= 0 || numCh <= 0)
133 return;
134
135 // Block-level gain smoothing setup
136 const T targetGain = gain_.load(std::memory_order_relaxed);
137 const T startGain = currentGain_;
138 const bool needsSmoothing = std::abs(targetGain - startGain) > T(1e-6);
139 const T gainStep = needsSmoothing ? ((targetGain - startGain) / static_cast<T>(numSamples)) : T(0);
140
141 for (int ch = 0; ch < numCh; ++ch)
142 {
143 T* data = buffer.getChannel(ch);
144 auto& gen = generators_[static_cast<size_t>(ch)];
145
146 if (needsSmoothing)
147 {
148 T g = startGain;
149 for (int i = 0; i < numSamples; ++i)
150 {
151 g += gainStep;
152 data[i] = gen.getNextSample() * g;
153 }
154 }
155 else
156 {
157 for (int i = 0; i < numSamples; ++i)
158 {
159 data[i] = gen.getNextSample() * targetGain;
160 }
161 }
162 }
163
164 // Advance global smoothed state for the next block
165 currentGain_ = targetGain;
166 }
167
176 void reset() noexcept
177 {
178 int ch = 0;
179 for (auto& gen : generators_)
180 {
181 gen.reset();
182 gen.reseed(static_cast<uint64_t>(++ch) * 0x9E3779B97F4A7C15ULL);
183 }
184
185 currentGain_ = gain_.load(std::memory_order_relaxed);
186 }
187
196 void setType(Type type) noexcept
197 {
198 // Wild enum values (a corrupted blob, a stray cast) clamp into range.
199 const int t = std::clamp(static_cast<int>(type), 0,
200 static_cast<int>(Type::Brown));
201 type_.store(static_cast<Type>(t), std::memory_order_relaxed);
202 typeDirty_.store(true, std::memory_order_release);
203 }
204
210 void setLevel(T levelDb) noexcept
211 {
212 if (std::isnan(levelDb) || levelDb > T(1e6)) return; // rejects +inf too
213 setGain(decibelsToGain(levelDb)); // -inf (<= -100 dB) maps to 0
214 }
215
221 void setGain(T gain) noexcept
222 {
223 if (!std::isfinite(gain)) return;
224 gain_.store(gain, std::memory_order_relaxed);
225 }
226
231 [[nodiscard]] Type getType() const noexcept
232 {
233 return type_.load(std::memory_order_relaxed);
234 }
235
240 [[nodiscard]] T getGain() const noexcept
241 {
242 return gain_.load(std::memory_order_relaxed);
243 }
244
245
247 [[nodiscard]] std::vector<uint8_t> getState() const
248 {
249 StateWriter w(stateId("NOIS"), 1);
250 w.write("gain", gain_.load(std::memory_order_relaxed));
251 w.write("type", static_cast<int32_t>(type_.load(std::memory_order_relaxed)));
252 return w.blob();
253 }
254
256 bool setState(const uint8_t* data, size_t size)
257 {
258 StateReader r(data, size);
259 if (!r.isValid() || r.processorId() != stateId("NOIS")) return false;
260 setGain(static_cast<T>(r.read("gain", 1.0f)));
261 setType(static_cast<Type>(r.read("type", 0)));
262 return true;
263 }
264
265private:
266 void applyNoiseType(AnalogRandom::Generator<T>& gen) const noexcept
267 {
268 switch (type_.load(std::memory_order_relaxed))
269 {
270 case Type::White:
271 gen.setNoiseType(AnalogRandom::NoiseType::White);
272 break;
273 case Type::Pink:
274 gen.setNoiseType(AnalogRandom::NoiseType::Pink);
275 break;
276 case Type::Brown:
277 gen.setNoiseType(AnalogRandom::NoiseType::Brown);
278 break;
279 }
280 }
281
282 double sampleRate_ = 44100.0;
283
284 // Core parameters (Thread-safe)
285 std::atomic<T> gain_ { T(1) };
286 std::atomic<Type> type_ { Type::White };
287 std::atomic<bool> typeDirty_ { false };
288
289 // Internal state (Audio thread only)
290 T currentGain_ { T(1) };
291
292 // Value semantics for memory locality. Avoids pointer chasing.
293 std::vector<AnalogRandom::Generator<T>> generators_;
294};
295
296} // namespace dspark
Main generator class for analog-style random modulation.
void setRateHz(Real rateInHz) noexcept
void reseed(std::uint64_t newSeed) noexcept
Request a lock-free reseed of the internal PRNG.
void prepare(double sampleRate) noexcept
Prepare the generator with the audio sample rate.
void setRange(T min, T max) noexcept
Set the output range for values. Safe from tearing.
Non-owning view over audio channel data.
Definition AudioBuffer.h:50
Generates decorellated noise (white, pink, brown) across multiple channels.
void setLevel(T levelDb) noexcept
Sets the output level in decibels. Thread-safe.
void reset() noexcept
Resets the internal PRNG and filter states.
void prepare(const AudioSpec &spec)
Prepares the noise generator and allocates internal state.
bool setState(const uint8_t *data, size_t size)
Restores parameters from a blob (tolerant; rejects foreign ids).
T getGain() const noexcept
Retrieves the current target gain in linear scale.
void setType(Type type) noexcept
Sets the noise spectrum type.
void setGain(T gain) noexcept
Sets the output level as linear gain. Thread-safe.
Type getType() const noexcept
Retrieves the currently requested noise type.
std::vector< uint8_t > getState() const
Serializes the parameter state (setup/UI threads; allocates).
void processBlock(AudioBufferView< T > buffer) noexcept
Fills the buffer with generated noise.
Tolerant reader: missing keys yield defaults, unknown keys are skipped.
Definition StateBlob.h:160
float read(const char *key, float defaultValue) const
Reads a float, or defaultValue when the key is absent.
Definition StateBlob.h:203
bool isValid() const noexcept
Definition StateBlob.h:198
uint32_t processorId() const noexcept
Definition StateBlob.h:199
Serializes key/value parameters into a versioned blob.
Definition StateBlob.h:52
std::vector< uint8_t > blob() const
Finalizes and returns the blob.
Definition StateBlob.h:104
void write(const char *key, float value)
Writes a float parameter.
Definition StateBlob.h:70
@ White
Uncorrelated noise for "sample-and-hold" effects.
@ Pink
1/f noise for organic drift.
@ Brown
1/f^2 noise for slow "wow/flutter".
Main namespace for the DSPark framework.
T decibelsToGain(T dB, T minusInfinityDb=T(-100)) noexcept
Converts a value in decibels to linear gain.
Definition DspMath.h:65
constexpr uint32_t stateId(const char(&tag)[5]) noexcept
Builds a FOURCC processor id, e.g. dspark::stateId("COMP").
Definition StateBlob.h:639
Describes the audio environment for a DSP processor.
Definition AudioSpec.h:35
constexpr bool isValid() const noexcept
Checks if the specification contains valid, processable parameters.
Definition AudioSpec.h:67
int numChannels
Number of audio channels (e.g., 1 = mono, 2 = stereo).
Definition AudioSpec.h:56
double sampleRate
Sample rate in Hz.
Definition AudioSpec.h:43