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.

#### 148 lines 5.6 KiB Raw Permalink Blame History

 ```/* AudioEffectCompressor2_F32.cpp ``` ``` * ``` ``` * Bob Larkin W7PUA 22 January 2021 ``` ``` * See AudioEffectCompressor2_F32.h for details ``` ``` * ``` ``` * MIT License. Use at your own risk. ``` ``` */ ``` ```#include "AudioEffectCompressor2_F32.h" ``` ``` ``` ``` /* See https://github.com/Tympan/Tympan_Library/blob/master/src/AudioCalcGainWDRC_F32.h ``` ``` * Dr Paul Beckmann ``` ``` * https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621 ``` ``` * Fast approximation to the log2() function. It uses a two step ``` ``` * process. First, it decomposes the floating-point number into ``` ``` * a fractional component F and an exponent E. The fraction component ``` ``` * is used in a polynomial approximation and then the exponent added ``` ``` * to the result. A 3rd order polynomial is used and the result ``` ``` * when computing db20() is accurate to 7.984884e-003 dB. Y is log2(X) ``` ``` */ ``` ``` float v2DB_Approx(float volts) { ``` ``` float Y, F; ``` ``` int E; ``` ``` // This is the approximation to log2() ``` ``` F = frexpf(volts, &E); // first separate power of 2; ``` ``` // Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E; ``` ``` Y = 1.23149591; //C[0] ``` ``` Y *= F; ``` ``` Y += -4.11852516f; //C[1] ``` ``` Y *= F; ``` ``` Y += 6.02197014f; //C[2] ``` ``` Y *= F; ``` ``` Y += -3.13396450f; //C[3] ``` ``` Y += E; ``` ``` // Convert to dB = 20 Log10(volts) ``` ``` return 6.020599f * Y; // (20.0f/log2(10.0))*Y; ``` ``` } ``` ``` ``` ``` // Accelerate the powf(10.0,x) function (from Chip's single slope compressor) ``` ``` float pow10f(float x) { ``` ``` //return powf(10.0f,x) //standard, but slower ``` ``` return expf(2.30258509f*x); //faster: exp(log(10.0f)*x) ``` ``` } ``` ``` ``` ```// begin() ``` ```void AudioEffectCompressor2_F32::begin(void) { ``` ``` for(int kk =0; kk<5; kk++) // Keeps division out of update() ``` ``` slope[kk] = 1.0f/curve0.compressionRatio[kk]; ``` ``` ``` ``` outKneeDB[0] = curve0.marginDB; // Start at top ``` ``` for(int kk=1; kk<5; kk++) { ``` ``` outKneeDB[kk] = outKneeDB[kk-1] - (curve0.kneeDB[kk-1] - ``` ``` curve0.kneeDB[kk])/curve0.compressionRatio[kk-1]; ``` ``` } ``` ``` ``` ``` firstIndex = 4; // Start at bottom ``` ``` for(int kk=4; kk>0; kk--) { ``` ``` if(curve0.kneeDB[kk] >= -500.0) { ``` ``` firstIndex = kk; ``` ``` break; ``` ``` } ``` ``` } ``` ``` /* for(int kk=0; kk<5; kk++) { ``` ``` Serial.print(kk); ``` ``` Serial.print(" <--k outKneeDB[k]--> "); ``` ``` Serial.println(outKneeDB[kk]); ``` ``` } ``` ``` Serial.print("firstIndex--> "); ``` ``` Serial.println(firstIndex); ``` ``` */ ``` ``` } ``` ``` ``` ``` // update() ``` ``` void AudioEffectCompressor2_F32::update(void) { ``` ``` float vAbs, vPeak; ``` ``` float vInDB = 0.0f; ``` ``` float vOutDB = 0.0f; ``` ``` float targetGain; ``` ``` ``` ``` // Receive the input audio data ``` ``` audio_block_f32_t *block = AudioStream_F32::receiveWritable_f32(); ``` ``` if (!block) return; ``` ``` // Allocate memory for the output ``` ``` audio_block_f32_t *out_block = AudioStream_F32::allocate_f32(); ``` ``` if (!out_block) { ``` ``` release(block); ``` ``` return; ``` ``` } ``` ``` ``` ``` // Find the smoothed envelope, target gain and compressed output ``` ``` vPeak = vPeakSave; ``` ``` for (int k=0; klength; k++) { ``` ``` vAbs = (block->data[k] >= 0.0f) ? block->data[k] : -block->data[k]; ``` ``` if (vAbs >= vPeak) { // Attack (rising level) ``` ``` vPeak = alpha * vPeak + (oneMinusAlpha) * vAbs; ``` ``` } else { // Release (decay for falling level) ``` ``` vPeak = beta * vPeak; ``` ``` } ``` ``` // Convert to dB ``` ``` // At all levels and quite frequency flat, this under estimates by about 1.05 dB ``` ``` vInDB = v2DB_Approx(vPeak) + 1.05f; ``` ``` if(vInDB > vInMaxDB) vInMaxDB = vInDB; // For reporting back ``` ``` ``` ``` // Find gain point. Don't look below first segment firstIndex. ``` ``` for(int kk=firstIndex; kk>=0; kk--) { ``` ``` if( vInDB<=curve0.kneeDB[kk] || kk==0 ) { ``` ``` vOutDB = outKneeDB[kk] + slope[kk]*(vInDB - curve0.kneeDB[kk]); ``` ``` break; ``` ``` } ``` ``` } ``` ``` // Convert the needed gain back to a voltage ratio 10^(db/20) ``` ``` targetGain = pow10f(0.05f*(vOutDB - vInDB)); ``` ``` // And apply target gain to signal stream from the delayed data. The ``` ``` // delay buffer is circular because of delayBufferMask and length 2^m m<=8. ``` ``` out_block->data[k] = targetGain * delayData[(k + in_index) & delayBufferMask]; ``` ``` ``` ``` if(printIO) { ``` ``` Serial.print(block->data[k],6); ``` ``` Serial.print("," ); ``` ``` Serial.print(delayData[(k + in_index) & delayBufferMask],6); ``` ``` Serial.print("," ); ``` ``` Serial.println(targetGain); ``` ``` } ``` ``` ``` ``` // Put the new data into the delay line, delaySize positions ahead. ``` ``` // If delaySize==256, this will be the same location as we just got data from. ``` ``` delayData[(k + in_index + delaySize) & delayBufferMask] = block->data[k]; ``` ``` } ``` ``` vPeakSave = vPeak; // save last vPeak for next time ``` ``` sampleInputDB = vInDB; // Last values for get...() functions ``` ``` sampleGainDB = vOutDB - vInDB; ``` ``` // transmit the block and release memory ``` ``` AudioStream_F32::release(block); ``` ``` AudioStream_F32::transmit(out_block); // send the FIR output ``` ``` AudioStream_F32::release(out_block); ``` ``` // Update pointer in_index to delay line for next 128 update ``` ``` in_index = (in_index + block->length) & delayBufferMask; ``` ``` } // End update() ``` ``` ``` ``` // Sets a new compression curve by transferring structure ``` ``` void AudioEffectCompressor2_F32::setCompressionCurve(struct compressionCurve *_cCurve) { ``` ``` curve0.marginDB = _cCurve->marginDB; ``` ``` curve0.offsetDB = _cCurve->offsetDB; ``` ``` for(int kk=0; kk<5; kk++) { ``` ``` // Also, adjust the input levels for offsetDB value ``` ``` curve0.kneeDB[kk] = _cCurve->kneeDB[kk] - curve0.offsetDB; ``` ``` curve0.compressionRatio[kk] = _cCurve->compressionRatio[kk]; ``` ``` } ``` ``` } ``` ``` ```