You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
4.5 KiB
144 lines
4.5 KiB
/* Audio Library for Teensy 3.X
|
|
* Dynamics Processor (Gate, Compressor & Limiter)
|
|
* Copyright (c) 2017, Marc Paquette (marc@dacsystemes.com)
|
|
* Based on analyse_rms & mixer objects by Paul Stoffregen
|
|
*
|
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
|
* open source software by purchasing Teensy or other PJRC products.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice, development funding notice, and this permission
|
|
* notice shall be included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "effect_dynamics.h"
|
|
#include "fast_log.h"
|
|
#include "utility/dspinst.h"
|
|
#include "utility/sqrt_integer.h"
|
|
|
|
static float analyse_rms(int16_t *data) {
|
|
|
|
uint32_t *p = (uint32_t *)data;
|
|
const uint32_t *end = p + AUDIO_BLOCK_SAMPLES / 2;
|
|
int64_t sum = 0;
|
|
do {
|
|
uint32_t n1 = *p++;
|
|
uint32_t n2 = *p++;
|
|
uint32_t n3 = *p++;
|
|
uint32_t n4 = *p++;
|
|
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n1, n1);
|
|
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n2, n2);
|
|
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n3, n3);
|
|
sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n4, n4);
|
|
|
|
} while (p < end);
|
|
|
|
int32_t meansq = sum / AUDIO_BLOCK_SAMPLES;
|
|
return sqrt_uint32(meansq) / 32767.0f;
|
|
}
|
|
|
|
static void applyGain(int16_t *data, int32_t mult1, int32_t mult2) {
|
|
|
|
uint32_t *p = (uint32_t *)data;
|
|
const uint32_t *end = p + AUDIO_BLOCK_SAMPLES / 2;
|
|
int32_t inc = (mult2 - mult1) / (AUDIO_BLOCK_SAMPLES / 2);
|
|
|
|
do {
|
|
uint32_t tmp32 = *p; // read 2 samples from *data
|
|
int32_t val1 = signed_multiply_32x16b(mult1, tmp32);
|
|
mult1 += inc;
|
|
int32_t val2 = signed_multiply_32x16t(mult1, tmp32);
|
|
mult1 += inc;
|
|
val1 = signed_saturate_rshift(val1, 16, 0);
|
|
val2 = signed_saturate_rshift(val2, 16, 0);
|
|
*p++ = pack_16b_16b(val2, val1);
|
|
} while (p < end);
|
|
}
|
|
|
|
void AudioEffectDynamics::update(void) {
|
|
|
|
audio_block_t *block;
|
|
|
|
block = receiveWritable(0);
|
|
|
|
if (!block) return;
|
|
|
|
if (!gateEnabled && !compEnabled && !limiterEnabled) {
|
|
|
|
//Transmit & release
|
|
transmit(block);
|
|
release(block);
|
|
return;
|
|
}
|
|
|
|
//Analyze received block
|
|
float rms = analyse_rms(block->data);
|
|
|
|
//Compute block RMS level in Db
|
|
float inputdb = MIN_DB;
|
|
if (rms > 0) inputdb = unitToDb(rms);
|
|
|
|
//Gate
|
|
if (gateEnabled) {
|
|
if (inputdb >= gateThresholdOpen) gatedb = (aGateAttack * gatedb) + (aOneMinusGateAttack * MAX_DB);
|
|
else if (inputdb < gateThresholdClose) gatedb = (aGateRelease * gatedb) + (aOneMinusGateRelease * MIN_DB);
|
|
}
|
|
else gatedb = MAX_DB;
|
|
|
|
//Compressor
|
|
if (compEnabled) {
|
|
float attdb = MAX_DB; //Below knee
|
|
if (inputdb >= aLowKnee) {
|
|
if(inputdb <= aHighKnee) {
|
|
//Knee transition
|
|
float knee = inputdb - aLowKnee;
|
|
attdb = aKneeRatio * knee * knee * aTwoKneeWidth;
|
|
}
|
|
else {
|
|
//Above knee
|
|
attdb = compThreshold + ((inputdb - compThreshold) * compRatio) - inputdb;
|
|
}
|
|
}
|
|
if (attdb <= compdb) compdb = (aCompAttack * compdb) + (aOneMinusCompAttack * attdb);
|
|
else compdb = (aCompRelease * compdb) + (aOneMinusCompRelease * attdb);
|
|
}
|
|
else compdb = MAX_DB;
|
|
|
|
//Brickwall Limiter
|
|
if (limiterEnabled) {
|
|
float outdb = inputdb + compdb + makeupdb;
|
|
if (outdb >= limitThreshold) limitdb = (aLimitAttack * limitdb) + (aOneMinusLimitAttack * (limitThreshold - outdb));
|
|
else limitdb *= aLimitRelease;
|
|
}
|
|
else limitdb = MAX_DB;
|
|
|
|
//Compute linear gain
|
|
float totalGain = gatedb + compdb + makeupdb + limitdb;
|
|
int32_t mult = dbToUnit(totalGain) * 65536.0f;
|
|
|
|
//Apply gain to block
|
|
applyGain(block->data, last_mult, mult);
|
|
last_mult = mult;
|
|
|
|
//Transmit & release
|
|
transmit(block);
|
|
release(block);
|
|
}
|
|
|
|
|
|
|
|
|