signal.cpp (11451B)
1 2 #include <assert.h> 3 #include <algorithm> 4 #include <cmath> 5 6 #include "signal.hpp" 7 8 using namespace bogaudio::dsp; 9 10 const float Amplifier::minDecibels = -60.0f; 11 const float Amplifier::maxDecibels = 20.0f; 12 const float Amplifier::decibelsRange = maxDecibels - minDecibels; 13 14 void Amplifier::LevelTable::_generate() { 15 const float rdb = 6.0f; 16 const float tdb = Amplifier::minDecibels + rdb; 17 const float ta = decibelsToAmplitude(tdb); 18 _table[0] = 0.0f; 19 for (int i = 1; i < _length; ++i) { 20 float db = Amplifier::minDecibels + (i / (float)_length) * Amplifier::decibelsRange; 21 if (db <= tdb) { 22 _table[i] = ((db - minDecibels) / rdb) * ta; 23 } 24 else { 25 _table[i] = decibelsToAmplitude(db); 26 } 27 } 28 } 29 30 void Amplifier::setLevel(float db) { 31 if (_db != db) { 32 _db = db; 33 if (_db > minDecibels) { 34 if (_db < maxDecibels) { 35 _level = _table.value(((_db - minDecibels) / decibelsRange) * _table.length()); 36 } 37 else { 38 _level = decibelsToAmplitude(_db); 39 } 40 } 41 else { 42 _level = 0.0f; 43 } 44 } 45 } 46 47 float Amplifier::next(float s) { 48 return _level * s; 49 } 50 51 52 void RunningAverage::setSampleRate(float sampleRate) { 53 assert(sampleRate > 0.0f); 54 if (_sampleRate != sampleRate) { 55 _sampleRate = sampleRate; 56 if (_buffer) { 57 delete[] _buffer; 58 } 59 _bufferN = (_maxDelayMS / 1000.0f) * _sampleRate; 60 _buffer = new float[_bufferN] {}; 61 if (_initialized) { 62 _initialized = false; 63 setSensitivity(_sensitivity); 64 } 65 } 66 } 67 68 void RunningAverage::setSensitivity(float sensitivity) { 69 assert(sensitivity >= 0.0f); 70 assert(sensitivity <= 1.0f); 71 if (_initialized) { 72 if (_sensitivity != sensitivity) { 73 _sensitivity = sensitivity; 74 int newSumN = std::max(_sensitivity * _bufferN, 1.0f); 75 int i = newSumN; 76 while (i > _sumN) { 77 --_trailI; 78 if (_trailI < 0) { 79 _trailI = _bufferN - 1; 80 } 81 _sum += _buffer[_trailI]; 82 --i; 83 } 84 while (i < _sumN) { 85 _sum -= _buffer[_trailI]; 86 ++_trailI; 87 _trailI %= _bufferN; 88 ++i; 89 } 90 _sumN = newSumN; 91 } 92 } 93 else { 94 _initialized = true; 95 _sensitivity = sensitivity; 96 _sumN = std::max(_sensitivity * _bufferN, 1.0f); 97 _leadI = 0; 98 _trailI = _bufferN - _sumN; 99 _sum = 0.0; 100 } 101 _invSumN = 1.0f / (float)_sumN; 102 } 103 104 void RunningAverage::reset() { 105 _sum = 0.0; 106 std::fill(_buffer, _buffer + _bufferN, 0.0); 107 } 108 109 float RunningAverage::next(float sample) { 110 _sum -= _buffer[_trailI]; 111 ++_trailI; 112 _trailI %= _bufferN; 113 _sum += _buffer[_leadI] = sample; 114 ++_leadI; 115 _leadI %= _bufferN; 116 return (float)_sum * _invSumN; 117 } 118 119 120 bool PositiveZeroCrossing::next(float sample) { 121 switch (_state) { 122 case NEGATIVE_STATE: { 123 if (sample > positiveThreshold) { 124 _state = POSITIVE_STATE; 125 return true; 126 } 127 break; 128 } 129 case POSITIVE_STATE: { 130 if (sample < negativeThreshold) { 131 _state = NEGATIVE_STATE; 132 } 133 else if (sample < positiveThreshold && _triggerable) { 134 _state = COUNT_ZEROES_STATE; 135 _zeroCount = 1; 136 } 137 break; 138 } 139 case COUNT_ZEROES_STATE: { 140 if (sample >= negativeThreshold) { 141 if (++_zeroCount >= zeroesForReset) { 142 _state = NEGATIVE_STATE; 143 } 144 } 145 else { 146 _state = NEGATIVE_STATE; 147 } 148 break; 149 } 150 } 151 return false; 152 } 153 154 void PositiveZeroCrossing::reset() { 155 _state = NEGATIVE_STATE; 156 } 157 158 159 void SlewLimiter::setParams(float sampleRate, float milliseconds, float range) { 160 assert(sampleRate > 0.0f); 161 assert(milliseconds >= 0.0f); 162 assert(range > 0.0f); 163 _delta = range / ((milliseconds / 1000.0f) * sampleRate); 164 } 165 166 float SlewLimiter::next(float sample, float last) { 167 if (sample > last) { 168 return std::min(last + _delta, sample); 169 } 170 return std::max(last - _delta, sample); 171 } 172 173 174 void ShapedSlewLimiter::setParams(float sampleRate, float milliseconds, float shape) { 175 assert(sampleRate > 0.0f); 176 assert(milliseconds >= 0.0f); 177 assert(shape >= minShape); 178 assert(shape <= maxShape); 179 _sampleTime = 1.0f / sampleRate; 180 _time = milliseconds / 1000.0f; 181 _shapeExponent = (shape > -0.05f && shape < 0.05f) ? 0.0f : shape; 182 _inverseShapeExponent = 1.0f / _shapeExponent; 183 } 184 185 float ShapedSlewLimiter::next(float sample) { 186 double difference = sample - _last; 187 double ttg = fabs(difference) / range; 188 if (_time < 0.0001f) { 189 return _last = sample; 190 } 191 if (_shapeExponent != 0.0f) { 192 ttg = pow(ttg, _shapeExponent); 193 } 194 ttg *= _time; 195 ttg = std::max(0.0, ttg - _sampleTime); 196 ttg /= _time; 197 if (_shapeExponent != 0.0f) { 198 ttg = pow(ttg, _inverseShapeExponent); 199 } 200 double y = fabs(difference) - ttg * range; 201 if (difference < 0.0f) { 202 return _last = std::max(_last - y, (double)sample); 203 } 204 return _last = std::min(_last + y, (double)sample); 205 } 206 207 208 void Integrator::setParams(float alpha) { 209 assert(alpha >= 0.0f); 210 assert(alpha <= 1.0f); 211 _alpha = alpha; 212 } 213 214 float Integrator::next(float sample) { 215 // "leaky integrator" 216 return _last = (1.0f - _alpha)*_last + _alpha*sample; 217 } 218 219 220 void CrossFader::setParams(float mix, float curve, bool linear) { 221 assert(mix >= -1.0f && mix <= 1.0f); 222 assert(curve >= -1.0f && curve <= 1.0f); 223 if (_mix != mix || _curve != curve || _linear != linear) { 224 _mix = mix; 225 _curve = curve; 226 _linear = linear; 227 228 float aMax, aMin; 229 float bMax, bMin; 230 if (_curve < 0.0f) { 231 aMax = 0.0f; 232 aMin = _curve + 2.0f; 233 bMax = 2.0f; 234 bMin = 0.0f - _curve; 235 } 236 else { 237 aMax = _curve; 238 aMin = 2.0f; 239 bMax = 2.0f - _curve; 240 bMin = 0.0f; 241 } 242 243 float m = _mix + 1.0f; 244 if (m < aMax) { 245 _aMix = 1.0f; 246 } 247 else if (m > aMin) { 248 _aMix = 0.0f; 249 } 250 else { 251 _aMix = 1.0f - ((m - aMax) / (aMin - aMax)); 252 } 253 254 if (m > bMax) { 255 _bMix = 1.0f; 256 } 257 else if (m < bMin) { 258 _bMix = 0.0f; 259 } 260 else { 261 _bMix = (m - bMin) / (bMax - bMin); 262 } 263 264 if (!_linear) { 265 _aAmp.setLevel((1.0f - _aMix) * Amplifier::minDecibels); 266 _bAmp.setLevel((1.0f - _bMix) * Amplifier::minDecibels); 267 } 268 } 269 } 270 271 float CrossFader::next(float a, float b) { 272 if (_linear) { 273 return _aMix * a + _bMix * b; 274 } 275 return _aAmp.next(a) + _bAmp.next(b); 276 } 277 278 279 void Panner::setPan(float pan) { 280 assert(pan >= -1.0f); 281 assert(pan <= 1.0f); 282 if (_pan != pan) { 283 _pan = pan; 284 _lLevel = _sineTable.value(((1.0f + _pan) / 8.0f + 0.25f) * _sineTable.length()); 285 _rLevel = _sineTable.value(((1.0f + _pan) / 8.0f) * _sineTable.length()); 286 } 287 } 288 289 void Panner::next(float sample, float& l, float& r) { 290 l = _lLevel * sample; 291 r = _rLevel * sample; 292 } 293 294 295 void DelayLine::setSampleRate(float sampleRate) { 296 assert(sampleRate > 0.0f); 297 if (_sampleRate != sampleRate) { 298 _sampleRate = sampleRate; 299 if (_buffer) { 300 delete[] _buffer; 301 } 302 _bufferN = ceil((_maxTimeMS / 1000.0f) * _sampleRate); 303 _buffer = new float[_bufferN] {}; 304 if (_initialized) { 305 _initialized = false; 306 setTime(_time); 307 } 308 } 309 } 310 311 void DelayLine::setTime(float time) { 312 assert(time >= 0.0f); 313 assert(time <= 1.0f); 314 if (_initialized) { 315 if (_time != time) { 316 _time = time; 317 int newDelaySamples = delaySamples(); 318 int i = newDelaySamples; 319 while (i > _delaySamples) { 320 --_trailI; 321 if (_trailI < 0) { 322 _trailI = _bufferN - 1; 323 } 324 --i; 325 } 326 while (i < _delaySamples) { 327 ++_trailI; 328 _trailI %= _bufferN; 329 ++i; 330 } 331 _delaySamples = newDelaySamples; 332 } 333 } 334 else { 335 _initialized = true; 336 _time = time; 337 _delaySamples = delaySamples(); 338 _leadI = 0; 339 _trailI = _bufferN - _delaySamples; 340 } 341 } 342 343 float DelayLine::next(float sample) { 344 float delayed = _buffer[_trailI]; 345 ++_trailI; 346 _trailI %= _bufferN; 347 _buffer[_leadI] = sample; 348 ++_leadI; 349 _leadI %= _bufferN; 350 return delayed; 351 } 352 353 int DelayLine::delaySamples() { 354 return std::max((_time * _maxTimeMS / 1000.0f) * _sampleRate, 1.0f); 355 } 356 357 358 void Limiter::setParams(float shape, float knee, float limit, float scale) { 359 assert(shape >= 0.0f); 360 assert(knee >= 0.0f); 361 assert(limit >= 0.0f); 362 assert(scale >= 1.0f); 363 _shape = shape; 364 _knee = knee; 365 _limit = std::max(knee, limit); 366 _scale = scale; 367 368 if (_shape >= 0.1f) { 369 if (_shape < 1.0f) { 370 _normalization = 1.0f / tanhf(_shape * M_PI); 371 } 372 else { 373 _normalization = 1.0f; 374 } 375 } 376 } 377 378 float Limiter::next(float sample) { 379 float out = fabsf(sample); 380 if (out > _knee) { 381 out -= _knee; 382 out /= _scale; 383 if (_shape >= 0.1f) { 384 // out /= _limit - _knee; 385 // out = _tanhf.value(out * _shape * M_PI) * _normalization; 386 // out *= _limit - _knee; 387 float x = out / (_limit - _knee); 388 x = _tanhf.value(x * _shape * M_PI) * _normalization; 389 x = std::min(x, 1.0f); 390 x *= _limit - _knee; 391 out = std::min(fabsf(sample) - _knee, x); 392 } 393 else { 394 out = std::min(out, _limit - _knee); 395 } 396 out += _knee; 397 } 398 399 if (sample < 0.0f) { 400 return -out; 401 } 402 return out; 403 } 404 405 406 const float Saturator::limit = 12.0f; 407 408 // Zavalishin 2018, "The Art of VA Filter Design", http://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_2.0.0a.pdf 409 static inline float saturation(float x) { 410 const float y1 = 0.98765f; // (2*x - 1)/x**2 where x is 0.9. 411 const float offset = 0.075f / Saturator::limit; // magic. 412 float x1 = (x + 1.0f) * 0.5f; 413 return Saturator::limit * (offset + x1 - sqrtf(x1 * x1 - y1 * x) * (1.0f / y1)); 414 } 415 416 float Saturator::next(float sample) { 417 float x = sample * (1.0f / limit); 418 if (sample < 0.0f) { 419 return -saturation(-x); 420 } 421 return saturation(x); 422 } 423 424 425 const float Compressor::maxEffectiveRatio = 1000.0f; 426 427 float Compressor::compressionDb(float detectorDb, float thresholdDb, float ratio, bool softKnee) { 428 const float softKneeDb = 3.0f; 429 430 if (softKnee) { 431 float sDb = thresholdDb - softKneeDb; 432 if (detectorDb <= sDb) { 433 return 0.0f; 434 } 435 436 float ix = softKneeDb * std::min(ratio, maxEffectiveRatio) + thresholdDb; 437 float iy = softKneeDb + thresholdDb; 438 float t = (detectorDb - sDb) / (ix - thresholdDb); 439 float px = t * (ix - thresholdDb) + thresholdDb; 440 float py = t * (iy - thresholdDb) + thresholdDb; 441 float s = (py - sDb) / (px - sDb); 442 float compressionDb = detectorDb - sDb; 443 compressionDb -= s * (detectorDb - sDb); 444 return compressionDb; 445 } 446 447 if (detectorDb <= thresholdDb) { 448 return 0.0f; 449 } 450 float compressionDb = detectorDb - thresholdDb; 451 compressionDb -= compressionDb / ratio; 452 return compressionDb; 453 } 454 455 456 const float NoiseGate::maxEffectiveRatio = Compressor::maxEffectiveRatio; 457 458 float NoiseGate::compressionDb(float detectorDb, float thresholdDb, float ratio, bool softKnee) { 459 const float softKneeDb = 6.0f; 460 461 if (softKnee) { 462 // FIXME: this achieves nothing. 463 float range = thresholdDb - Amplifier::minDecibels; 464 float ix = thresholdDb + softKneeDb; 465 float iy = 0; 466 if (detectorDb >= ix) { 467 return 0.0f; 468 } 469 float ox = thresholdDb - range / ratio; 470 if (detectorDb <= ox) { 471 return -Amplifier::minDecibels; 472 } 473 const float oy = Amplifier::minDecibels; 474 float t = (detectorDb - ox) / (ix - ox); 475 float px = t * (ix - thresholdDb) + thresholdDb; 476 float py = t * (iy - thresholdDb) + thresholdDb; 477 float s = (py - oy) / (px - ox); 478 return -(oy + s * (detectorDb - ox)); 479 } 480 481 if (detectorDb >= thresholdDb) { 482 return 0.0f; 483 } 484 float differenceDb = thresholdDb - detectorDb; 485 float compressionDb = differenceDb * ratio - differenceDb; 486 return std::min(compressionDb, -Amplifier::minDecibels); 487 } 488 489 490 void Timer::setParams(float sampleRate, float time) { 491 assert(sampleRate > 0.0f); 492 assert(time >= 0.0f); 493 // FIXME: if the timer is running, should set the duration to reflect the time elapsed so far, adjusting it for the delta samplerate. 494 _durationSteps = time * sampleRate; 495 } 496 497 void Timer::reset() { 498 _expired = false; 499 _countSteps = 0; 500 } 501 502 bool Timer::next() { 503 ++_countSteps; 504 _expired = _expired || _countSteps >= _durationSteps; 505 return !_expired; 506 }