arduino-audio-tools
Loading...
Searching...
No Matches
soft_float_t.h
Go to the documentation of this file.
1#pragma once
2
3#include <cmath>
4#include <cstdint>
6
7namespace audio_tools {
8
47 public:
48 constexpr soft_float_t() = default;
49 constexpr soft_float_t(int16_t mantissa, int16_t exponent)
50 : m_(mantissa), e_(exponent) {}
51 soft_float_t(float f) { fromFloat(f); }
52 soft_float_t(double d) { fromFloat((float)d); }
53 soft_float_t(int i) { fromFloat((float)i); }
54
55 operator float() const { return toFloat(); }
57 explicit operator int() const {
58 float v = toFloat();
59 if (v >= 2147483647.0f) return INT32_MAX;
60 if (v <= -2147483648.0f) return INT32_MIN;
61 return (int)v;
62 }
63
64 // PCM samples are interpreted at their raw magnitude (e.g. up to +-32767
65 // for 16-bit audio) rather than normalized, matching the convention used
66 // when T=float elsewhere in this codebase -- soft_float_t's wide dynamic
67 // range (unlike q1_14_t's bounded +-2.0) means no rescaling is needed.
68
70 int16_t toInt16() const { return (int16_t)clampRound(toFloat(), -32768.0, 32767.0); }
72 int24_t toInt24() const { return clampRound(toFloat(), -8388608.0, 8388607.0); }
74 int32_t toInt32() const { return clampRound(toFloat(), -2147483648.0, 2147483647.0); }
75
77 static soft_float_t fromInt16(int16_t sample) { return soft_float_t((float)sample); }
80 return soft_float_t((float)(int32_t)sample);
81 }
83 static soft_float_t fromInt32(int32_t sample) { return soft_float_t((float)sample); }
84
85 // Applies this value as a gain/volume factor to a full-scale PCM sample
86 // (sample * factor), clipping on overflow. Unlike operator*, the sample
87 // here is NOT itself a soft_float_t-scaled value.
88
90 int16_t scale(int16_t sample) const {
91 return (int16_t)clampRound(toFloat() * (double)sample, -32768.0, 32767.0);
92 }
94 int24_t scale(int24_t sample) const {
95 return clampRound(toFloat() * (double)(int32_t)sample, -8388608.0, 8388607.0);
96 }
98 int32_t scale(int32_t sample) const {
99 return clampRound(toFloat() * (double)sample, -2147483648.0, 2147483647.0);
100 }
101
103 return addAligned(m_, e_, o.m_, o.e_);
104 }
105 soft_float_t operator-(soft_float_t o) const { return *this + (-o); }
107 return soft_float_t((int16_t)(-(int32_t)m_), e_);
108 }
109
111 int32_t prod = ((int32_t)m_ * (int32_t)o.m_) >> 15;
112 return normalize(prod, (int32_t)e_ + o.e_ + 15);
113 }
114
116 if (o.m_ == 0) {
117 // Match WORD2INT-style saturation elsewhere in this codebase: a very
118 // large (but finite, sign-matching) result instead of a hardware trap.
119 return soft_float_t((int16_t)(m_ < 0 ? -32767 : 32767), (int16_t)30);
120 }
121 int32_t numerator = (int32_t)m_ << 15;
122 int32_t quotient = numerator / o.m_;
123 return normalize(quotient, (int32_t)e_ - o.e_ - 15);
124 }
125
126 soft_float_t& operator+=(soft_float_t o) { return *this = *this + o; }
127 soft_float_t& operator-=(soft_float_t o) { return *this = *this - o; }
128 soft_float_t& operator*=(soft_float_t o) { return *this = *this * o; }
129 soft_float_t& operator/=(soft_float_t o) { return *this = *this / o; }
130
131 bool operator<(soft_float_t o) const { return toFloat() < o.toFloat(); }
132 bool operator>(soft_float_t o) const { return toFloat() > o.toFloat(); }
133 bool operator<=(soft_float_t o) const { return toFloat() <= o.toFloat(); }
134 bool operator>=(soft_float_t o) const { return toFloat() >= o.toFloat(); }
135 bool operator==(soft_float_t o) const { return m_ == o.m_ && e_ == o.e_; }
136 bool operator!=(soft_float_t o) const { return !(*this == o); }
137
138 // soft_float_t has both a converting constructor from float and a
139 // conversion operator to float, so `pseudoFloatValue * 0.7f` (and every
140 // other mixed op) would otherwise be ambiguous -- the compiler can't
141 // choose between converting the float to soft_float_t (to use the member
142 // operator above) or converting the soft_float_t to float (to use the
143 // built-in operator), and both need exactly one user-defined conversion.
144 // These exact-match overloads (no conversion needed for either operand)
145 // resolve that ambiguity, since the algorithm this class backs mixes
146 // float literals and soft_float_t variables constantly.
147 soft_float_t operator+(float o) const { return *this + soft_float_t(o); }
148 soft_float_t operator-(float o) const { return *this - soft_float_t(o); }
149 soft_float_t operator*(float o) const { return *this * soft_float_t(o); }
150 soft_float_t operator/(float o) const { return *this / soft_float_t(o); }
151 bool operator<(float o) const { return *this < soft_float_t(o); }
152 bool operator>(float o) const { return *this > soft_float_t(o); }
153 bool operator<=(float o) const { return *this <= soft_float_t(o); }
154 bool operator>=(float o) const { return *this >= soft_float_t(o); }
155 bool operator==(float o) const { return *this == soft_float_t(o); }
156 bool operator!=(float o) const { return *this != soft_float_t(o); }
157
158 // Same exact-match-ambiguity issue as the float overloads above, but for
159 // `int` literals (e.g. `1 - pseudoFloatValue`, `radius > 16383`): without
160 // these, the compiler has to choose between the float overload (needs an
161 // int->float conversion) and the built-in int operator (reachable from
162 // soft_float_t via the implicit operator float() followed by a
163 // floating-integral conversion), and neither dominates the other.
164 soft_float_t operator+(int o) const { return *this + soft_float_t(o); }
165 soft_float_t operator-(int o) const { return *this - soft_float_t(o); }
166 soft_float_t operator*(int o) const { return *this * soft_float_t(o); }
167 soft_float_t operator/(int o) const { return *this / soft_float_t(o); }
168 bool operator<(int o) const { return *this < soft_float_t(o); }
169 bool operator>(int o) const { return *this > soft_float_t(o); }
170 bool operator<=(int o) const { return *this <= soft_float_t(o); }
171 bool operator>=(int o) const { return *this >= soft_float_t(o); }
172 bool operator==(int o) const { return *this == soft_float_t(o); }
173 bool operator!=(int o) const { return *this != soft_float_t(o); }
174
175 // Same exact-match-ambiguity issue again, but for `double` literals --
176 // e.g. unsuffixed literals like `1.` or `.5`, or macros such as the
177 // Arduino core's `PI` (ArduinoCore-API/api/Common.h defines it as an
178 // unsuffixed double, so it stays double even on real hardware, not just
179 // desktop builds).
180 soft_float_t operator+(double o) const { return *this + soft_float_t(o); }
181 soft_float_t operator-(double o) const { return *this - soft_float_t(o); }
182 soft_float_t operator*(double o) const { return *this * soft_float_t(o); }
183 soft_float_t operator/(double o) const { return *this / soft_float_t(o); }
184 bool operator<(double o) const { return *this < soft_float_t(o); }
185 bool operator>(double o) const { return *this > soft_float_t(o); }
186 bool operator<=(double o) const { return *this <= soft_float_t(o); }
187 bool operator>=(double o) const { return *this >= soft_float_t(o); }
188 bool operator==(double o) const { return *this == soft_float_t(o); }
189 bool operator!=(double o) const { return *this != soft_float_t(o); }
190
191 int16_t mantissa() const { return m_; }
192 int16_t exponent() const { return e_; }
193
194 private:
195 int16_t m_ = 0; // normalized magnitude in [16384, 32767] (0 iff value == 0)
196 int16_t e_ = 0; // value = m_ * 2^e_
197
200 static int32_t clampRound(double v, double lo, double hi) {
201 if (v >= hi) return (int32_t)hi;
202 if (v <= lo) return (int32_t)lo;
203 v += (v >= 0.0 ? 0.5 : -0.5);
204 return (int32_t)v;
205 }
206
207 void fromFloat(float f) {
208 if (f == 0.0f) {
209 m_ = 0;
210 e_ = 0;
211 return;
212 }
213 bool neg = f < 0;
214 double mag = neg ? -(double)f : (double)f;
215 int32_t e = 0;
216 while (mag >= 32768.0) {
217 mag *= 0.5;
218 e++;
219 }
220 while (mag < 16384.0) {
221 mag *= 2.0;
222 e--;
223 }
224 int16_t mag16 = (int16_t)mag;
225 m_ = neg ? (int16_t)(-(int32_t)mag16) : mag16;
226 e_ = (int16_t)e;
227 }
228
229 float toFloat() const {
230 return (float)((double)m_ * pow(2.0, (double)e_));
231 }
232
235 static soft_float_t normalize(int32_t val, int32_t e) {
236 if (val == 0) return soft_float_t((int16_t)0, (int16_t)0);
237 bool neg = val < 0;
238 int64_t mag = neg ? -(int64_t)val : (int64_t)val;
239 while (mag > 32767) {
240 mag >>= 1;
241 e++;
242 }
243 while (mag < 16384) {
244 mag <<= 1;
245 e--;
246 }
247 int16_t mi = (int16_t)(neg ? -mag : mag);
248 return soft_float_t(mi, (int16_t)e);
249 }
250
253 static soft_float_t addAligned(int16_t ma, int16_t ea, int16_t mb,
254 int16_t eb) {
255 if (ma == 0) return soft_float_t(mb, eb);
256 if (mb == 0) return soft_float_t(ma, ea);
257 int32_t a = ma, b = mb;
258 int32_t e;
259 if (ea >= eb) {
260 int shift = ea - eb;
261 b = (shift >= 31) ? 0 : (b >> shift);
262 e = ea;
263 } else {
264 int shift = eb - ea;
265 a = (shift >= 31) ? 0 : (a >> shift);
266 e = eb;
267 }
268 return normalize(a + b, e);
269 }
270};
271
272// Free-function overloads for `float op soft_float_t` (float on the left),
273// for the same exact-match-ambiguity reason as the member float overloads
274// above.
275inline soft_float_t operator+(float a, soft_float_t b) { return soft_float_t(a) + b; }
276inline soft_float_t operator-(float a, soft_float_t b) { return soft_float_t(a) - b; }
277inline soft_float_t operator*(float a, soft_float_t b) { return soft_float_t(a) * b; }
278inline soft_float_t operator/(float a, soft_float_t b) { return soft_float_t(a) / b; }
279inline bool operator<(float a, soft_float_t b) { return soft_float_t(a) < b; }
280inline bool operator>(float a, soft_float_t b) { return soft_float_t(a) > b; }
281inline bool operator<=(float a, soft_float_t b) { return soft_float_t(a) <= b; }
282inline bool operator>=(float a, soft_float_t b) { return soft_float_t(a) >= b; }
283inline bool operator==(float a, soft_float_t b) { return soft_float_t(a) == b; }
284inline bool operator!=(float a, soft_float_t b) { return soft_float_t(a) != b; }
285
286// Free-function overloads for `int op soft_float_t` (int on the left), for
287// the same reason as the free `float` overloads above.
288inline soft_float_t operator+(int a, soft_float_t b) { return soft_float_t(a) + b; }
289inline soft_float_t operator-(int a, soft_float_t b) { return soft_float_t(a) - b; }
290inline soft_float_t operator*(int a, soft_float_t b) { return soft_float_t(a) * b; }
291inline soft_float_t operator/(int a, soft_float_t b) { return soft_float_t(a) / b; }
292inline bool operator<(int a, soft_float_t b) { return soft_float_t(a) < b; }
293inline bool operator>(int a, soft_float_t b) { return soft_float_t(a) > b; }
294inline bool operator<=(int a, soft_float_t b) { return soft_float_t(a) <= b; }
295inline bool operator>=(int a, soft_float_t b) { return soft_float_t(a) >= b; }
296inline bool operator==(int a, soft_float_t b) { return soft_float_t(a) == b; }
297inline bool operator!=(int a, soft_float_t b) { return soft_float_t(a) != b; }
298
299// Free-function overloads for `double op soft_float_t` (double on the
300// left), for the same reason as the free `float`/`int` overloads above.
301inline soft_float_t operator+(double a, soft_float_t b) { return soft_float_t(a) + b; }
302inline soft_float_t operator-(double a, soft_float_t b) { return soft_float_t(a) - b; }
303inline soft_float_t operator*(double a, soft_float_t b) { return soft_float_t(a) * b; }
304inline soft_float_t operator/(double a, soft_float_t b) { return soft_float_t(a) / b; }
305inline bool operator<(double a, soft_float_t b) { return soft_float_t(a) < b; }
306inline bool operator>(double a, soft_float_t b) { return soft_float_t(a) > b; }
307inline bool operator<=(double a, soft_float_t b) { return soft_float_t(a) <= b; }
308inline bool operator>=(double a, soft_float_t b) { return soft_float_t(a) >= b; }
309inline bool operator==(double a, soft_float_t b) { return soft_float_t(a) == b; }
310inline bool operator!=(double a, soft_float_t b) { return soft_float_t(a) != b; }
311
314
315} // namespace audio_tools
24bit integer which is used for I2S sound processing. The values are represented as int32_t,...
Definition int24_4bytes_t.h:22
Software "pseudo-float": a real number represented as a signed 16-bit mantissa plus a 16-bit exponent...
Definition soft_float_t.h:46
bool operator>=(soft_float_t o) const
Definition soft_float_t.h:134
static soft_float_t fromInt24(int24_t sample)
builds a soft_float_t from a 24-bit PCM sample (-8388608..8388607)
Definition soft_float_t.h:79
soft_float_t & operator+=(soft_float_t o)
Definition soft_float_t.h:126
bool operator>=(int o) const
Definition soft_float_t.h:171
static soft_float_t fromInt32(int32_t sample)
builds a soft_float_t from a 32-bit PCM sample (-2147483648..2147483647)
Definition soft_float_t.h:83
bool operator<=(float o) const
Definition soft_float_t.h:153
bool operator!=(double o) const
Definition soft_float_t.h:189
soft_float_t operator/(float o) const
Definition soft_float_t.h:150
bool operator>=(double o) const
Definition soft_float_t.h:187
int16_t exponent() const
Definition soft_float_t.h:192
bool operator<(double o) const
Definition soft_float_t.h:184
soft_float_t operator-(double o) const
Definition soft_float_t.h:181
bool operator>(float o) const
Definition soft_float_t.h:152
bool operator<(int o) const
Definition soft_float_t.h:168
int32_t toInt32() const
converts to a 32-bit PCM sample (-2147483648..2147483647)
Definition soft_float_t.h:74
constexpr soft_float_t()=default
int24_t toInt24() const
converts to a 24-bit PCM sample (-8388608..8388607)
Definition soft_float_t.h:72
int24_t scale(int24_t sample) const
scales a 24-bit PCM sample by this factor
Definition soft_float_t.h:94
soft_float_t operator-(soft_float_t o) const
Definition soft_float_t.h:105
bool operator<(soft_float_t o) const
Definition soft_float_t.h:131
bool operator==(float o) const
Definition soft_float_t.h:155
soft_float_t(int i)
Definition soft_float_t.h:53
soft_float_t operator*(soft_float_t o) const
Definition soft_float_t.h:110
soft_float_t operator/(double o) const
Definition soft_float_t.h:183
bool operator!=(soft_float_t o) const
Definition soft_float_t.h:136
soft_float_t operator+(double o) const
Definition soft_float_t.h:180
int16_t mantissa() const
Definition soft_float_t.h:191
static soft_float_t fromInt16(int16_t sample)
builds a soft_float_t from a 16-bit PCM sample (-32768..32767)
Definition soft_float_t.h:77
soft_float_t operator+(float o) const
Definition soft_float_t.h:147
soft_float_t & operator*=(soft_float_t o)
Definition soft_float_t.h:128
constexpr soft_float_t(int16_t mantissa, int16_t exponent)
Definition soft_float_t.h:49
soft_float_t & operator/=(soft_float_t o)
Definition soft_float_t.h:129
int32_t scale(int32_t sample) const
scales a 32-bit PCM sample by this factor
Definition soft_float_t.h:98
soft_float_t operator/(soft_float_t o) const
Definition soft_float_t.h:115
bool operator>(int o) const
Definition soft_float_t.h:169
int16_t scale(int16_t sample) const
scales a 16-bit PCM sample by this factor
Definition soft_float_t.h:90
bool operator<(float o) const
Definition soft_float_t.h:151
soft_float_t operator*(float o) const
Definition soft_float_t.h:149
bool operator==(int o) const
Definition soft_float_t.h:172
bool operator>(double o) const
Definition soft_float_t.h:185
bool operator<=(soft_float_t o) const
Definition soft_float_t.h:133
int16_t toInt16() const
converts to a 16-bit PCM sample (-32768..32767)
Definition soft_float_t.h:70
soft_float_t operator/(int o) const
Definition soft_float_t.h:167
soft_float_t operator*(double o) const
Definition soft_float_t.h:182
bool operator<=(double o) const
Definition soft_float_t.h:186
bool operator!=(int o) const
Definition soft_float_t.h:173
soft_float_t operator-(float o) const
Definition soft_float_t.h:148
bool operator==(double o) const
Definition soft_float_t.h:188
soft_float_t(double d)
Definition soft_float_t.h:52
bool operator>=(float o) const
Definition soft_float_t.h:154
bool operator==(soft_float_t o) const
Definition soft_float_t.h:135
bool operator<=(int o) const
Definition soft_float_t.h:170
soft_float_t & operator-=(soft_float_t o)
Definition soft_float_t.h:127
soft_float_t operator-() const
Definition soft_float_t.h:106
soft_float_t operator+(int o) const
Definition soft_float_t.h:164
soft_float_t operator+(soft_float_t o) const
Definition soft_float_t.h:102
soft_float_t operator-(int o) const
Definition soft_float_t.h:165
soft_float_t(float f)
Definition soft_float_t.h:51
soft_float_t operator*(int o) const
Definition soft_float_t.h:166
bool operator>(soft_float_t o) const
Definition soft_float_t.h:132
bool operator!=(float o) const
Definition soft_float_t.h:156
uint8_t pow(uint8_t x, intmax_t power)
Definition gf.hpp:111
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
float operator+(float16 one, float16 two)
Definition float16.h:102
bool operator<(float a, q1_14_t b)
Definition q1_14_t.h:186
bool operator>=(float a, q1_14_t b)
Definition q1_14_t.h:189
static bool operator!=(audio_tools::digital_pin_t &a, audio_tools::digital_pin_t &b)
Support for pin compare.
Definition Arduino.h:281
float operator/(float16 one, float16 two)
Definition float16.h:111
float operator-(float16 one, float16 two)
Definition float16.h:105
static bool operator==(audio_tools::digital_pin_t &a, audio_tools::digital_pin_t &b)
Support for pin compare.
Definition Arduino.h:276
bool operator>(float a, q1_14_t b)
Definition q1_14_t.h:188
bool operator<=(float a, q1_14_t b)
Definition q1_14_t.h:187
float operator*(float16 one, float16 two)
Definition float16.h:108