Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
lfsr.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <bit>
5#include <cmath>
6#include <cstddef>
7#include <cstdint>
8#include <limits>
9#include <type_traits>
10
11namespace pulp::signal {
12
33template <typename SampleType = float> class LfsrT {
34 static_assert(std::is_floating_point_v<SampleType>);
35
36 public:
37 static constexpr int kMinLength = 2;
38 static constexpr int kMaxLength = 32;
39 static constexpr int kDefaultLength = 8;
40 static constexpr std::uint32_t kDefaultFeedbackMask = 0x8Eu;
41 static constexpr std::uint32_t kDefaultSeed = 0b10110100u;
42
43 bool configure(int length, std::uint32_t feedback_mask, std::uint32_t seed) noexcept {
44 if (!valid_length(length) || !mask_fits(feedback_mask, length) ||
45 !projection_is_finite_for(length))
46 return false;
47 length_ = length;
48 feedback_mask_ = feedback_mask;
49 seed_ = seed & register_mask();
50 state_ = seed_;
51 return true;
52 }
53
54 bool set_length(int length) noexcept {
55 if (!valid_length(length) || !projection_is_finite_for(length))
56 return false;
57 length_ = length;
58 feedback_mask_ &= register_mask();
59 seed_ &= register_mask();
60 state_ = seed_;
61 return true;
62 }
63
64 bool set_feedback_mask(std::uint32_t mask) noexcept {
65 if (!mask_fits(mask, length_))
66 return false;
67 feedback_mask_ = mask;
68 return true;
69 }
70
71 void set_seed(std::uint32_t seed) noexcept {
72 seed_ = seed & register_mask();
73 reset();
74 }
75
76 bool set_weight(std::size_t bit, SampleType weight) noexcept {
77 if (bit >= static_cast<std::size_t>(length_) || !std::isfinite(static_cast<double>(weight)))
78 return false;
79 const SampleType previous = weights_[bit];
80 weights_[bit] = weight;
81 if (!projection_is_finite()) {
82 weights_[bit] = previous;
83 return false;
84 }
85 return true;
86 }
87
88 void clear_weights() noexcept {
89 weights_.fill(SampleType{});
90 }
91
92 bool set_offset(SampleType offset) noexcept {
93 if (!std::isfinite(static_cast<double>(offset)))
94 return false;
95 const SampleType previous = offset_;
96 offset_ = offset;
97 if (!projection_is_finite()) {
98 offset_ = previous;
99 return false;
100 }
101 return true;
102 }
103
104 void reset() noexcept {
105 state_ = seed_;
106 }
107
108 std::uint32_t clock(bool external_bit = false) noexcept {
109 const auto parity = std::popcount(state_ & feedback_mask_) & 1;
110 const auto next_bit = static_cast<std::uint32_t>(parity ^ (external_bit ? 1 : 0));
111 state_ = ((state_ << 1u) | next_bit) & register_mask();
112 return state_;
113 }
114
115 SampleType weighted_output() const noexcept {
116 long double output = static_cast<long double>(offset_);
117 for (int bit = 0; bit < length_; ++bit) {
118 if ((state_ & (std::uint32_t{1} << bit)) != 0u)
119 output += static_cast<long double>(weights_[static_cast<std::size_t>(bit)]);
120 }
121 return static_cast<SampleType>(output);
122 }
123
124 SampleType minimum_output() const noexcept {
125 long double output = static_cast<long double>(offset_);
126 for (int bit = 0; bit < length_; ++bit)
127 if (weights_[static_cast<std::size_t>(bit)] < SampleType{})
128 output += static_cast<long double>(weights_[static_cast<std::size_t>(bit)]);
129 return static_cast<SampleType>(output);
130 }
131
132 SampleType maximum_output() const noexcept {
133 long double output = static_cast<long double>(offset_);
134 for (int bit = 0; bit < length_; ++bit)
135 if (weights_[static_cast<std::size_t>(bit)] > SampleType{})
136 output += static_cast<long double>(weights_[static_cast<std::size_t>(bit)]);
137 return static_cast<SampleType>(output);
138 }
139
140 int length() const noexcept {
141 return length_;
142 }
143 std::uint32_t feedback_mask() const noexcept {
144 return feedback_mask_;
145 }
146 std::uint32_t seed() const noexcept {
147 return seed_;
148 }
149 std::uint32_t state() const noexcept {
150 return state_;
151 }
152 SampleType offset() const noexcept {
153 return offset_;
154 }
155
156 static constexpr int latency_samples() noexcept {
157 return 0;
158 }
159 static constexpr int tail_samples() noexcept {
160 return 0;
161 }
162
163 private:
164 static constexpr bool valid_length(int length) noexcept {
165 return length >= kMinLength && length <= kMaxLength;
166 }
167
168 static constexpr std::uint32_t mask_for(int length) noexcept {
169 return length == 32 ? 0xFFFFFFFFu : ((std::uint32_t{1} << length) - 1u);
170 }
171
172 static constexpr bool mask_fits(std::uint32_t mask, int length) noexcept {
173 return valid_length(length) && (mask & ~mask_for(length)) == 0u;
174 }
175
176 constexpr std::uint32_t register_mask() const noexcept {
177 return mask_for(length_);
178 }
179
180 bool projection_is_finite_for(int length) const noexcept {
181 long double minimum = static_cast<long double>(offset_);
182 long double maximum = minimum;
183 for (int bit = 0; bit < length; ++bit) {
184 const long double weight =
185 static_cast<long double>(weights_[static_cast<std::size_t>(bit)]);
186 if (weight < 0.0L)
187 minimum += weight;
188 if (weight > 0.0L)
189 maximum += weight;
190 }
191 const long double limit = static_cast<long double>(std::numeric_limits<SampleType>::max());
192 return minimum >= -limit && maximum <= limit;
193 }
194
195 bool projection_is_finite() const noexcept {
196 return projection_is_finite_for(length_);
197 }
198
199 std::array<SampleType, kMaxLength> weights_{};
200 SampleType offset_{};
201 int length_ = kDefaultLength;
202 std::uint32_t feedback_mask_ = kDefaultFeedbackMask;
203 std::uint32_t seed_ = kDefaultSeed;
204 std::uint32_t state_ = kDefaultSeed;
205};
206
209
210} // namespace pulp::signal
Configurable Fibonacci linear-feedback shift register with a weighted register-to-output projection.
Definition lfsr.hpp:33
bool set_feedback_mask(std::uint32_t mask) noexcept
Definition lfsr.hpp:64
static constexpr int tail_samples() noexcept
Definition lfsr.hpp:159
bool set_offset(SampleType offset) noexcept
Definition lfsr.hpp:92
bool set_weight(std::size_t bit, SampleType weight) noexcept
Definition lfsr.hpp:76
bool configure(int length, std::uint32_t feedback_mask, std::uint32_t seed) noexcept
Definition lfsr.hpp:43
SampleType maximum_output() const noexcept
Definition lfsr.hpp:132
SampleType minimum_output() const noexcept
Definition lfsr.hpp:124
int length() const noexcept
Definition lfsr.hpp:140
static constexpr std::uint32_t kDefaultFeedbackMask
Definition lfsr.hpp:40
SampleType weighted_output() const noexcept
Definition lfsr.hpp:115
static constexpr int kMinLength
Definition lfsr.hpp:37
static constexpr std::uint32_t kDefaultSeed
Definition lfsr.hpp:41
std::uint32_t clock(bool external_bit=false) noexcept
Definition lfsr.hpp:108
std::uint32_t seed() const noexcept
Definition lfsr.hpp:146
bool set_length(int length) noexcept
Definition lfsr.hpp:54
float offset() const noexcept
Definition lfsr.hpp:152
void clear_weights() noexcept
Definition lfsr.hpp:88
static constexpr int kDefaultLength
Definition lfsr.hpp:39
static constexpr int kMaxLength
Definition lfsr.hpp:38
void set_seed(std::uint32_t seed) noexcept
Definition lfsr.hpp:71
void reset() noexcept
Definition lfsr.hpp:104
static constexpr int latency_samples() noexcept
Definition lfsr.hpp:156
std::uint32_t state() const noexcept
Definition lfsr.hpp:149
std::uint32_t feedback_mask() const noexcept
Definition lfsr.hpp:143
Definition additive_bank.hpp:218
LfsrT< float > Lfsr
Definition lfsr.hpp:207
@ maximum
Definition spectrum_trace.hpp:34
@ u
Definition formant_filter_bank.hpp:20
LfsrT< double > Lfsr64
Definition lfsr.hpp:208
float minimum(const float *data, std::size_t count) noexcept