BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

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 }