parent
0e6fb86f77
commit
c140ca25b9
@ -0,0 +1,148 @@ |
|||||||
|
/* 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; k<block->length; 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]; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,225 @@ |
|||||||
|
/*
|
||||||
|
* AudioEffectCompressor2_F32.h |
||||||
|
* |
||||||
|
* Bob Larkin W7PUA 11 December 2020 |
||||||
|
* |
||||||
|
* This is a general purpose audio compressor block (C++ class). It works by determining |
||||||
|
* the average input of the input signal, and based on a pre-determined curve, |
||||||
|
* changes the gain going through the block. |
||||||
|
* A good discussion is the Wikipedia page: |
||||||
|
* https://en.wikipedia.org/wiki/Dynamic_range_compression
|
||||||
|
* This compressor includes up to 5 dB/dB line segments allowing for most of the |
||||||
|
* features listed. These include |
||||||
|
* Multi segment compression curves, up to 5 |
||||||
|
* Limiting |
||||||
|
* Approximation to "soft knees" |
||||||
|
* Expansion for suppressing low-level artifacts |
||||||
|
* Anticipation |
||||||
|
* Scale offset for use such as hearing-aid audiology |
||||||
|
* This is derived from the WDRC compressor. Chip Audette (OpenAudio) Feb 2017 |
||||||
|
* Which was derived From: WDRC_circuit from CHAPRO from BTNRC: https://github.com/BTNRH/chapro
|
||||||
|
* As of Feb 2017, CHAPRO license is listed as "Creative Commons?" |
||||||
|
* |
||||||
|
* MIT License. Use at your own risk. |
||||||
|
*/ |
||||||
|
/* Compressor #2. Amplifies input signals by varying amoounts depending
|
||||||
|
* on the signal level. This is controlled by up to 5 line segments specified |
||||||
|
* by a point at the highest input level for the line along with a compression ratio |
||||||
|
* (1/slope) for the line segment. This can provide limiting at the highest inputs |
||||||
|
* by setting compressionRatio[0]=1000.0 (i.e., some big value). Expansion at the |
||||||
|
* lw levels provides a squelch action, using a very small compression |
||||||
|
* ratio like 0.01. |
||||||
|
* |
||||||
|
* A special case is the [0] segment, that continues at the same slope for inputs up |
||||||
|
* to any level. For this the kneeDB[0] can be the expected 0.0 or other values |
||||||
|
* on that line. The output level for kneeDB[0] is the input variable, marginDB. |
||||||
|
* This allows gain control near clipping but below. marginDB is typically 1 or 2 dB. |
||||||
|
* |
||||||
|
Vout dB |
||||||
|
| |
||||||
|
| |
||||||
|
0.0 + kneeDB[0] |
||||||
|
marg + kneeDB[1] @******************** |
||||||
|
| @************ 1:compressionRatio[0] |
||||||
|
| **** |
||||||
|
| *** |
||||||
|
| *** |
||||||
|
| *** 1:compressionRatio[1] |
||||||
|
| kneeDB[2] *** |
||||||
|
| @*** |
||||||
|
| * |
||||||
|
| * |
||||||
|
| * |
||||||
|
| * |
||||||
|
| * 1:compressionRatio[2] |
||||||
|
| * |
||||||
|
| * === Vout in dB vs. Vin in dB === |
||||||
|
|* Three segment example |
||||||
|
* Knees (breakpoints) are shown with '@' |
||||||
|
*| compressionRatio[] are ratio of: input change (in dB):output change in dB |
||||||
|
* | |
||||||
|
* |________|___________________|____________________________|________ vIn dB |
||||||
|
k1 k2 0.0 |
||||||
|
|
||||||
|
* The graph shows the changes in gain on a log or dB scale. A compressionRatio |
||||||
|
* of 1 represents a constant gain with level. When the compressionRatio is greater |
||||||
|
* than 1, say 2.0, the voltage gain is decreasing as the input level increases. |
||||||
|
* |
||||||
|
* vInDB refers to the time averaged envelope voltage. |
||||||
|
* The zero reference is the full ADC range output. This is ±1.0 |
||||||
|
* peak or 0.707 RMS in F32 terminology. |
||||||
|
* |
||||||
|
* The curve is for gainOffsetDB = 0.0. This parameter raises and lowers the |
||||||
|
* input scale for the kneeDB[] parameter. |
||||||
|
* |
||||||
|
* Timing: For 44.1 kHz sample rate and 256 samples per update, the update( ) time |
||||||
|
* runs 240 to 270 icroseconds using Teensy 3.6. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _AUDIO_EFFECT_COMPRESSOR2_F32_H |
||||||
|
#define _AUDIO_EFFECT_COMPRESSOR2_F32_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <AudioStream_F32.h> |
||||||
|
|
||||||
|
// The following 3 defines are simplified implementations for common uses.
|
||||||
|
// They replace the begin() function that is otherwise required.
|
||||||
|
// See testCompressor2.ino example for how to use these defines.
|
||||||
|
// None of these support offsetting the input scale as done in Tympan.
|
||||||
|
|
||||||
|
/* limiterBegin(pointerObject, float marDB, float linearInDB) has 2 segments.
|
||||||
|
* It is linear up to an input of linearInDB (typically -15.0f) and |
||||||
|
* then virtually limits for higher input levels. The output level at |
||||||
|
* this point is marDB, the margin to prevent clipping, like -2 dB. |
||||||
|
* This is not a clipper with waveform distortion, but rather decreases |
||||||
|
* the gain, dB for dB, as the input increases in the limiter region. |
||||||
|
* pobject is a pointer to the INO AudioEffectCompressor2_F32 object. |
||||||
|
* This function replaces begin() for the AudioEffectCompressor2_F32 object. |
||||||
|
*/ |
||||||
|
#define limiterBegin(pobject, marDB, linearInDB) struct compressionCurve _curv={marDB,0.0f,{0.0,linearInDB,-1000.0f,-1000.0f,-1000.0f},{100.0,1.0f,1.0f,1.0f,1.0f}}; pobject->setCompressionCurve(&_curv); pobject->begin(); |
||||||
|
|
||||||
|
/* basicCompressorBegin has a 3 segments. It is linear up to an input linearInDB
|
||||||
|
* and then decreases gain according to compressionRatioDB up to an input -10 dB where it |
||||||
|
* is almost limited, with an increase of output level of 1 dB for a 10 dB increase |
||||||
|
* in input level. The output level at full input is 1 dB below full output. |
||||||
|
* This function replaces begin() for the AudioEffectCompressor2_F32 object. |
||||||
|
*/ |
||||||
|
#define basicCompressorBegin(pobject, linearInDB, compressionRatio) struct compressionCurve _curv={-1.0,0.0f,{0.0,-10.0f,linearInDB,-1000.0f,-1000.0f},{10.0f,compressionRatio,1.0f,1.0f,1.0f}}; pobject->setCompressionCurve(&_curv); pobject->begin(); |
||||||
|
|
||||||
|
/* squelchCompressorBegin is similar to basicCompression above, except that there is
|
||||||
|
* an expansion region for low levels. So, the call defines the four regions in |
||||||
|
* terms of the input levels. squelchInDB sets the lowest input level |
||||||
|
* before the squelching effect starts. */ |
||||||
|
#define squelchCompressorBegin(pobject, squelchInDB, linearInDB, compressionInDB, compressionRatio) struct compressionCurve _curv={-1.0,0.0f,{0.0,compressionInDB,linearInDB,squelchInDB,-1000.0f},{10.0,compressionRatio,1.0f,0.1f,1.0f}}; pobject->setCompressionCurve(&_curv); pobject->begin(); |
||||||
|
|
||||||
|
// Basic definition of compression curve:
|
||||||
|
struct compressionCurve { |
||||||
|
float marginDB; |
||||||
|
float offsetDB; |
||||||
|
float kneeDB[5]; |
||||||
|
float compressionRatio[5]; |
||||||
|
}; |
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class AudioEffectCompressor2_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName: Compressor2
|
||||||
|
public: |
||||||
|
AudioEffectCompressor2_F32(void): AudioStream_F32(1, inputQueueArray) { |
||||||
|
setAttackReleaseSec(0.005f, 0.100f); |
||||||
|
} |
||||||
|
|
||||||
|
AudioEffectCompressor2_F32(const AudioSettings_F32 &settings): AudioStream_F32(1, inputQueueArray) { |
||||||
|
//setSampleRate_Hz(settings.sample_rate_Hz);
|
||||||
|
setAttackReleaseSec(0.005f, 0.100f); |
||||||
|
} |
||||||
|
|
||||||
|
virtual void update(void); |
||||||
|
void begin(void); |
||||||
|
void setCompressionCurve(struct compressionCurve*); |
||||||
|
// A delay of 256 samples is 256/44100 = 0.0058 sec = 5.8 mSec
|
||||||
|
void setDelayBufferSize(int16_t _delaySize) { // Any power of 2, i.e., 256, 128, 64, etc.
|
||||||
|
delaySize = _delaySize; |
||||||
|
delayBufferMask = _delaySize - 1; |
||||||
|
in_index = 0; |
||||||
|
} |
||||||
|
void printOn(bool _printIO) { printIO = _printIO; } // Diagnostics ONLY. Not for general INO
|
||||||
|
float getCurrentInputDB(void) { return sampleInputDB; } |
||||||
|
float getCurrentGainDB(void) { return sampleGainDB; } |
||||||
|
float getvInMaxDB(void) { |
||||||
|
float vRet = vInMaxDB; |
||||||
|
vInMaxDB = -1000.0f; // Reset for next max measure
|
||||||
|
return vRet; |
||||||
|
} |
||||||
|
//convert time constants from seconds to unitless parameters, from CHAPRO, agc_prepare.c
|
||||||
|
void setAttackReleaseSec(const float atk_sec, const float rel_sec) { |
||||||
|
// convert ANSI attack & release times to filter time constants
|
||||||
|
float ansi_atk = atk_sec * sample_rate_Hz / 2.425f; |
||||||
|
float ansi_rel = rel_sec * sample_rate_Hz / 1.782f; |
||||||
|
alpha = (float) (ansi_atk / (1.0f + ansi_atk)); |
||||||
|
oneMinusAlpha = 1.0f - alpha; |
||||||
|
beta = (float) (ansi_rel / (1.0f + ansi_rel)); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[1]; |
||||||
|
float delayData[256]; // The circular delay line for the signal
|
||||||
|
uint16_t in_index = 0; // Pointer to next block update entry
|
||||||
|
// And a mask to make the circular buffer limit to a power of 2
|
||||||
|
uint16_t delayBufferMask = 0X00FF; |
||||||
|
uint16_t delaySize = 256; |
||||||
|
|
||||||
|
float sample_rate_Hz = 44100; |
||||||
|
float attackSec = 0.005f; // Q: Can this be reduced with the delay line added to the signal path??
|
||||||
|
float releaseSec = 0.100f; |
||||||
|
// This alpha, beta for 5 ms attack, 100ms release, about 0.07 dB max ripple at 1000 Hz
|
||||||
|
float alpha = 0.98912216f; |
||||||
|
float oneMinusAlpha = 0.01087784f; |
||||||
|
float beta = 0.9995961f; |
||||||
|
|
||||||
|
/* Definition of the compression curve.
|
||||||
|
* Input and output are normally referenced to the full scale range (-1.0 to 1.0 in float). |
||||||
|
* The full scale point is called 0 dB. |
||||||
|
* There are 5 slopes, specified by the top input level for each line segment (kneeDB[]) |
||||||
|
* and the Compression Ratio (1/slope) of the segment (compressionRatio[]). |
||||||
|
* These are numbered from the highest to the lowest allowing |
||||||
|
* the number of segments to be adjusted, i.e., there is always a segment 0 |
||||||
|
* but there may not be a segment 3 and 4, for instance. This becomes very flexible, |
||||||
|
* as there can be, say, compression for top levels and expansion for very low levels. |
||||||
|
* |
||||||
|
* A special case is segment 0. On a plot of output dB vs input db, an input |
||||||
|
* level of kneeDB[0] produces an output of marginDB. A typical marginDB might |
||||||
|
* be -1.0 or -2.0. This margin allows controlling the gain without clipping in the DAC. |
||||||
|
* |
||||||
|
* This structure can have multiple curve structures like cCurve that can be used by |
||||||
|
* cmpr1.setCompressionCurve(&cCurve); |
||||||
|
* cmpr1.begin(); |
||||||
|
* |
||||||
|
* Unused segments can have knees like kneeDB[4] at, say -1000.0f. |
||||||
|
* Values below -500 will slightly speed up the update() function. if the bottom |
||||||
|
* two segments are not used, kneeDB[3] would also be set to -1000.0f amd so forth. |
||||||
|
* |
||||||
|
* Finally, there is a variable offsetDB that allows for a shift in the input scale. |
||||||
|
* It simply shifts the definition input levels, in dB. |
||||||
|
* This allows converting from DSP scales that have 0dB at full DSP scale |
||||||
|
* to auditory scales such as are used in the Tympan library. An offsetDB=119.0 |
||||||
|
* would allow all inputs to be in "SPL" units with a maximum input value of 119. |
||||||
|
*/ |
||||||
|
struct compressionCurve curve0 = { -2.0f, 0.0f, // margin, offset
|
||||||
|
{0.0f, -10.0f, -20.0f, -30.0f, -1000.0f}, // kneeDB[]
|
||||||
|
{ 100.0f, 2.0f, 1.5f, 1.0f, 1.0f} }; // compressionRatio
|
||||||
|
// VoutDB at each knee, needed to find gain; determined at begin():
|
||||||
|
float outKneeDB[5]={-2.0f, -3.0f, -8.0f -978.0f, -978.0f}; |
||||||
|
// slopes are 1/compressionRatio determined at begin() to save update{} time
|
||||||
|
float slope[5] = {0.01f, 0.5f, 0.666667f, 1.0f, 1.0f}; |
||||||
|
|
||||||
|
// Save time in update() by ignoring unused (low level) segments:
|
||||||
|
uint16_t firstIndex = 3; |
||||||
|
float vPeakSave = 0.0f; |
||||||
|
float vInMaxDB = -1000.0f; // Only for reporting
|
||||||
|
bool printIO = false; // Diagnostics Only
|
||||||
|
float sampleInputDB, sampleGainDB; |
||||||
|
}; |
||||||
|
#endif |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,173 @@ |
|||||||
|
/* TestCompressor2.ino Bob Larkin 23 January 2021
|
||||||
|
* |
||||||
|
* Test of AudioEffectCompressor2_F32 |
||||||
|
* See AudioEffectCompressor2_F32.h for much detail and explanation. |
||||||
|
* Choice of test signals is a single sine wave, a random sequence |
||||||
|
* of sine waves of varying frequency and amplitude, a power |
||||||
|
* sweep or a pulse of sine wave to see transient behavior. |
||||||
|
* |
||||||
|
* This version is for the Chip Audette OpenAudio_F32 Library. and |
||||||
|
* thus has that interface structure. |
||||||
|
* |
||||||
|
* NOTE: As of 23 January 2021, the compressor AudioEffectWDRC2_F32.h |
||||||
|
* was not finalized and could change in detail. Use here with |
||||||
|
* this in mind. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Audio.h" |
||||||
|
#include "OpenAudio_ArduinoLibrary.h" |
||||||
|
|
||||||
|
#define RANDOM 1 |
||||||
|
#define POWER_SWEEP 2 |
||||||
|
#define PULSE 3 |
||||||
|
#define ALTERNATE 4 |
||||||
|
// Edit in one of the last four, here:
|
||||||
|
#define SIGNAL_SOURCE RANDOM |
||||||
|
|
||||||
|
AudioSynthWaveformSine_F32 sine1; // Test signal
|
||||||
|
AudioEffectCompressor2_F32 compressor1; // Audio Compressor
|
||||||
|
AudioEffectGain_F32 gain0; // Sets volume sent to output
|
||||||
|
AudioEffectGain_F32 gain1; // Sets the same
|
||||||
|
AudioOutputI2S_F32 i2sOut; |
||||||
|
AudioConnection_F32 patchCord1(sine1, 0, compressor1, 0); |
||||||
|
AudioConnection_F32 patchCord2(compressor1, 0, gain0, 0); |
||||||
|
AudioConnection_F32 patchCord3(compressor1, 0, gain1, 0); |
||||||
|
AudioConnection_F32 patchCord4(gain0, 0, i2sOut, 0); |
||||||
|
AudioConnection_F32 patchCord5(gain1, 0, i2sOut, 1); |
||||||
|
AudioControlSGTL5000 sgtl5000_1; |
||||||
|
|
||||||
|
uint16_t count17, count27; |
||||||
|
float level = 0.05f; |
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
AudioMemory(50); |
||||||
|
AudioMemory_F32(100); |
||||||
|
Serial.begin(300); delay(1000); |
||||||
|
Serial.println("*** Test Compressor2 Gain Compressor **"); |
||||||
|
|
||||||
|
count17 = 0; |
||||||
|
count27 = 0; |
||||||
|
sine1.frequency(1000.0f); |
||||||
|
sine1.amplitude(0.05f); |
||||||
|
// CAUTION - If using ears on the output, adjust the following two carefully
|
||||||
|
gain0.setGain_dB(-25.0f); // Consider (-50.0f);
|
||||||
|
gain1.setGain_dB(-25.0f); // Consider (-50.0f);
|
||||||
|
sgtl5000_1.enable(); |
||||||
|
|
||||||
|
int16_t delaySize = 256; // Any power of 2, i.e., 256, 128, 64, etc.
|
||||||
|
compressor1.setDelayBufferSize(delaySize); |
||||||
|
|
||||||
|
// *** Here are four sample compressor curves. ***
|
||||||
|
// Select by number here, and re-compile.
|
||||||
|
#define COMPRESSOR_CURVE 1 |
||||||
|
|
||||||
|
#if COMPRESSOR_CURVE == 1 |
||||||
|
// Specify arbitrary 5-segment compression curve. An example of specifying
|
||||||
|
// compressionCurve. See AudioEffectCompressor2_F32.h for more details.
|
||||||
|
struct compressionCurve crv = { -2.0f, 0.0f, // margin, offset
|
||||||
|
{0.0f, -10.0f, -20.0f, -30.0f, -1000.0f}, // kneeDB[]
|
||||||
|
{ 100.0f, 2.5f, 1.5f, 1.0f, 1.0f} }; // compressionRatio
|
||||||
|
compressor1.setCompressionCurve(&crv); |
||||||
|
compressor1.begin(); |
||||||
|
#endif |
||||||
|
|
||||||
|
AudioEffectCompressor2_F32 *pc1 = &compressor1; // Needed for any *macro* defined curve.
|
||||||
|
// Defines pointer to compressor1, called pc1
|
||||||
|
|
||||||
|
#if COMPRESSOR_CURVE == 2 |
||||||
|
// Sample of a limiter at -3 dB out for highest 15 dB, limiter macro
|
||||||
|
limiterBegin(pc1, -3.0f, -15.0f); // pc1 is a pointer to compressor1 object
|
||||||
|
|
||||||
|
#elif COMPRESSOR_CURVE == 3 |
||||||
|
// Another one, a basic compressor curve:
|
||||||
|
// (pobject, linearInDB, compressionRatioDB) <- macro parameters
|
||||||
|
basicCompressorBegin(pc1, -25.0f, 2.0); |
||||||
|
|
||||||
|
#elif COMPRESSOR_CURVE == 4 |
||||||
|
// And one using a high gain region as a squelch (but no clicks or pops)
|
||||||
|
// (squelchIndB, linearInDB, compressionIndB, compressionRatioDB) <- macro parameters
|
||||||
|
squelchCompressorBegin(pc1, -40.0f, -25.0f, -10.0f, 1.5f); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) |
||||||
|
{ |
||||||
|
static uint16_t kk; |
||||||
|
|
||||||
|
#if SIGNAL_SOURCE == RANDOM |
||||||
|
/* To give an audio signal with interest, we alter the frequency
|
||||||
|
* every 17 blocks (49 msec) and alter the level every 27 b;ocks |
||||||
|
* (78.4 msec) The pattern keeps changing to be more interesting |
||||||
|
* Janet thinks it is aliens. */ |
||||||
|
|
||||||
|
delay(3); |
||||||
|
// Serial.print(" CurInDB= "); Serial.print( compressor1.getCurrentInputDB(), 3);
|
||||||
|
// Serial.print(" CurrentGainDB= "); Serial.println( compressor1.getCurrentGainDB(), 3);
|
||||||
|
// Serial.print("Maximum Input = "); Serial.println(compressor1.getvInMaxDB());
|
||||||
|
if(count17++ == 17) |
||||||
|
{ |
||||||
|
// Put a delay in, like between words
|
||||||
|
if(randUniform() < 0.03) |
||||||
|
delay( (int)(1500.0*randUniform()) ); |
||||||
|
count17 = 0; |
||||||
|
float ff = 350.0f + 700.0f*sqrtf( randUniform() ); |
||||||
|
sine1.frequency(ff); //Serial.println(ff);
|
||||||
|
} |
||||||
|
if(count27++ == 27) |
||||||
|
{ |
||||||
|
count27 = 0; |
||||||
|
level = 1.0f*powf( randUniform(), 2 ); // 0 to 1, emphasizing 0 end
|
||||||
|
sine1.amplitude(level); |
||||||
|
} |
||||||
|
|
||||||
|
#elif SIGNAL_SOURCE == POWER_SWEEP |
||||||
|
if(count17++ == 17) |
||||||
|
{ |
||||||
|
count17 = 0; |
||||||
|
level *= 1.0592537f; // 0.5 dB
|
||||||
|
delay(200); |
||||||
|
if(level > 1.0f) |
||||||
|
{ |
||||||
|
level=0.001f;
|
||||||
|
delay(500); |
||||||
|
} |
||||||
|
Serial.print( compressor1.getCurrentInputDB(), 3); |
||||||
|
Serial.print(","); Serial.println( compressor1.getCurrentGainDB(), 3); |
||||||
|
sine1.amplitude(level); |
||||||
|
} |
||||||
|
|
||||||
|
#elif SIGNAL_SOURCE == PULSE |
||||||
|
// A pulse, repeats every 3 minutes or so
|
||||||
|
delay(3); |
||||||
|
if(count17 == 5) sine1.amplitude(0.0f); // Settling
|
||||||
|
else if(count17 == 498) compressor1.printOn(true); //record it
|
||||||
|
else if(count17 == 500) sine1.amplitude(0.03f); |
||||||
|
else if(count17 == 510) sine1.amplitude(0.0f); |
||||||
|
else if(count17 == 700) compressor1.printOn(false); |
||||||
|
// or build your own transient test pulse here
|
||||||
|
count17++; |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/* randUniform() - Returns random number, uniform on (0, 1)
|
||||||
|
* The "Even Quicker" uniform random sample generator from D. E. Knuth and |
||||||
|
* H. W. Lewis and described in Chapter 7 of "Numerical Receipes in C", |
||||||
|
* 2nd ed, with the comment "this is about as good as any 32-bit linear |
||||||
|
* congruential generator, entirely adequate for many uses." |
||||||
|
*/ |
||||||
|
#define FL_ONE 0X3F800000 |
||||||
|
#define FL_MASK 0X007FFFFF |
||||||
|
float randUniform(void) |
||||||
|
{ |
||||||
|
static uint32_t idum = 12345; |
||||||
|
union { |
||||||
|
uint32_t i32; |
||||||
|
float f32; |
||||||
|
} uinf; |
||||||
|
|
||||||
|
idum = (uint32_t)1664525 * idum + (uint32_t)1013904223; |
||||||
|
// return (*(float *)&it); // Cute convert to float but gets compiler warning
|
||||||
|
uinf.i32 = FL_ONE | (FL_MASK & idum); // So do the same thing with a union
|
||||||
|
|
||||||
|
return uinf.f32 - 1.0f; |
||||||
|
} |
Loading…
Reference in new issue