/*
* File: Logical4.cpp
*
* Version: 1.0
*
* Created: 11/9/15
*
* Copyright: Copyright � 2015 Airwindows, All Rights Reserved
*
* Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
* consideration of your agreement to the following terms, and your use, installation, modification
* or redistribution of this Apple software constitutes acceptance of these terms. If you do
* not agree with these terms, please do not use, install, modify or redistribute this Apple
* software.
*
* In consideration of your agreement to abide by the following terms, and subject to these terms,
* Apple grants you a personal, non-exclusive license, under Apple's copyrights in this
* original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the
* Apple Software, with or without modifications, in source and/or binary forms; provided that if you
* redistribute the Apple Software in its entirety and without modifications, you must retain this
* notice and the following text and disclaimers in all such redistributions of the Apple Software.
* Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to
* endorse or promote products derived from the Apple Software without specific prior written
* permission from Apple. Except as expressly stated in this notice, no other rights or
* licenses, express or implied, are granted by Apple herein, including but not limited to any
* patent rights that may be infringed by your derivative works or by other works in which the
* Apple Software may be incorporated.
*
* The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR
* IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE
* OR IN COMBINATION WITH YOUR PRODUCTS.
*
* IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
* REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER
* UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN
* IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*=============================================================================
Logical4.cpp
=============================================================================*/
#include "Logical4.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
COMPONENT_ENTRY(Logical4)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Logical4::Logical4
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Logical4::Logical4(AudioUnit component)
: AUEffectBase(component)
{
CreateElements();
Globals()->UseIndexedParameters(kNumberOfParameters);
SetParameter(kParam_One, kDefaultValue_ParamOne );
SetParameter(kParam_Two, kDefaultValue_ParamTwo );
SetParameter(kParam_Three, kDefaultValue_ParamThree );
SetParameter(kParam_Four, kDefaultValue_ParamFour );
SetParameter(kParam_Five, kDefaultValue_ParamFive );
#if AU_DEBUG_DISPATCHER
mDebugDispatcher = new AUDebugDispatcher (this);
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Logical4::GetParameterValueStrings
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult Logical4::GetParameterValueStrings(AudioUnitScope inScope,
AudioUnitParameterID inParameterID,
CFArrayRef * outStrings)
{
return kAudioUnitErr_InvalidProperty;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Logical4::GetParameterInfo
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult Logical4::GetParameterInfo(AudioUnitScope inScope,
AudioUnitParameterID inParameterID,
AudioUnitParameterInfo &outParameterInfo )
{
ComponentResult result = noErr;
outParameterInfo.flags = kAudioUnitParameterFlag_IsWritable
| kAudioUnitParameterFlag_IsReadable;
if (inScope == kAudioUnitScope_Global) {
switch(inParameterID)
{
case kParam_One:
AUBase::FillInParameterName (outParameterInfo, kParameterOneName, false);
outParameterInfo.unit = kAudioUnitParameterUnit_Decibels;
outParameterInfo.minValue = -20.0;
outParameterInfo.maxValue = 20.0;
outParameterInfo.defaultValue = kDefaultValue_ParamOne;
break;
case kParam_Two:
AUBase::FillInParameterName (outParameterInfo, kParameterTwoName, false);
outParameterInfo.unit = kAudioUnitParameterUnit_CustomUnit;
outParameterInfo.flags |= kAudioUnitParameterFlag_DisplayLogarithmic;
outParameterInfo.unitName = kParameterTwoUnit;
outParameterInfo.minValue = 1.0;
outParameterInfo.maxValue = 16.0;
outParameterInfo.defaultValue = kDefaultValue_ParamTwo;
break;
case kParam_Three:
AUBase::FillInParameterName (outParameterInfo, kParameterThreeName, false);
outParameterInfo.unit = kAudioUnitParameterUnit_CustomUnit;
outParameterInfo.flags |= kAudioUnitParameterFlag_DisplayLogarithmic;
outParameterInfo.unitName = kParameterThreeUnit;
outParameterInfo.minValue = 1.0;
outParameterInfo.maxValue = 100.0;
outParameterInfo.defaultValue = kDefaultValue_ParamThree;
break;
case kParam_Four:
AUBase::FillInParameterName (outParameterInfo, kParameterFourName, false);
outParameterInfo.unit = kAudioUnitParameterUnit_Decibels;
outParameterInfo.minValue = -20.0;
outParameterInfo.maxValue = 20.0;
outParameterInfo.defaultValue = kDefaultValue_ParamFour;
break;
case kParam_Five:
AUBase::FillInParameterName (outParameterInfo, kParameterFiveName, false);
outParameterInfo.unit = kAudioUnitParameterUnit_Generic;
outParameterInfo.minValue = 0.0;
outParameterInfo.maxValue = 1.0;
outParameterInfo.defaultValue = kDefaultValue_ParamFive;
break;
default:
result = kAudioUnitErr_InvalidParameter;
break;
}
} else {
result = kAudioUnitErr_InvalidParameter;
}
return result;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Logical4::GetPropertyInfo
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult Logical4::GetPropertyInfo (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
UInt32 & outDataSize,
Boolean & outWritable)
{
return AUEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// state that plugin supports only stereo-in/stereo-out processing
UInt32 Logical4::SupportedNumChannels(const AUChannelInfo ** outInfo)
{
if (outInfo != NULL)
{
static AUChannelInfo info;
info.inChannels = 2;
info.outChannels = 2;
*outInfo = &info;
}
return 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Logical4::GetProperty
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult Logical4::GetProperty( AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
void * outData )
{
return AUEffectBase::GetProperty (inID, inScope, inElement, outData);
}
// Logical4::Initialize
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult Logical4::Initialize()
{
ComponentResult result = AUEffectBase::Initialize();
if (result == noErr)
Reset(kAudioUnitScope_Global, 0);
return result;
}
#pragma mark ____Logical4EffectKernel
//-----------------------------------------------------------------------------------------
// this is called the reset the DSP state (clear buffers, reset counters, etc.)
ComponentResult Logical4::Reset(AudioUnitScope inScope, AudioUnitElement inElement)
{
//begin ButterComps
controlAposL = 1.0;
controlAnegL = 1.0;
controlBposL = 1.0;
controlBnegL = 1.0;
targetposL = 1.0;
targetnegL = 1.0;
controlAposBL = 1.0;
controlAnegBL = 1.0;
controlBposBL = 1.0;
controlBnegBL = 1.0;
targetposBL = 1.0;
targetnegBL = 1.0;
controlAposCL = 1.0;
controlAnegCL = 1.0;
controlBposCL = 1.0;
controlBnegCL = 1.0;
targetposCL = 1.0;
targetnegCL = 1.0;
avgAL = avgBL = avgCL = avgDL = avgEL = avgFL = 0.0;
nvgAL = nvgBL = nvgCL = nvgDL = nvgEL = nvgFL = 0.0;
//end ButterComps
//begin ButterComps
controlAposR = 1.0;
controlAnegR = 1.0;
controlBposR = 1.0;
controlBnegR = 1.0;
targetposR = 1.0;
targetnegR = 1.0;
controlAposBR = 1.0;
controlAnegBR = 1.0;
controlBposBR = 1.0;
controlBnegBR = 1.0;
targetposBR = 1.0;
targetnegBR = 1.0;
controlAposCR = 1.0;
controlAnegCR = 1.0;
controlBposCR = 1.0;
controlBnegCR = 1.0;
targetposCR = 1.0;
targetnegCR = 1.0;
avgAR = avgBR = avgCR = avgDR = avgER = avgFR = 0.0;
nvgAR = nvgBR = nvgCR = nvgDR = nvgER = nvgFR = 0.0;
//end ButterComps
//begin Power Sags
for(int count = 0; count < 999; count++) {dL[count] = 0; bL[count] = 0; cL[count] = 0; dR[count] = 0; bR[count] = 0; cR[count] = 0;}
controlL = 0; controlBL = 0; controlCL = 0;
controlR = 0; controlBR = 0; controlCR = 0;
gcount = 0;
//end Power Sags
fpNShapeL = 0.0;
fpNShapeR = 0.0;
fpFlip = true;
return noErr;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// FarLogical43::ProcessBufferLists
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSStatus Logical4::ProcessBufferLists(AudioUnitRenderActionFlags & ioActionFlags,
const AudioBufferList & inBuffer,
AudioBufferList & outBuffer,
UInt32 inFramesToProcess)
{
Float32 * inputL = (Float32*)(inBuffer.mBuffers[0].mData);
Float32 * inputR = (Float32*)(inBuffer.mBuffers[1].mData);
Float32 * outputL = (Float32*)(outBuffer.mBuffers[0].mData);
Float32 * outputR = (Float32*)(outBuffer.mBuffers[1].mData);
Float64 overallscale = 1.0;
overallscale /= 44100.0;
overallscale *= GetSampleRate();
UInt32 nSampleFrames = inFramesToProcess;
Float32 drySampleL;
Float32 drySampleR;
long double inputSampleL;
long double inputSampleR;
long double fpOld = 0.618033988749894848204586; //golden ratio!
long double fpNew = 1.0 - fpOld;
//begin ButterComp
Float64 inputpos;
Float64 inputneg;
Float64 calcpos;
Float64 calcneg;
Float64 outputpos;
Float64 outputneg;
Float64 totalmultiplier;
Float64 inputgain = pow(10.0,(-GetParameter( kParam_One ))/20.0);
//fussing with the controls to make it hit the right threshold values
Float64 compoutgain = inputgain; //let's try compensating for this
Float64 attackspeed = GetParameter( kParam_Three );
//is in ms
attackspeed = 10.0 / sqrt(attackspeed);
//convert to a remainder for use in comp
Float64 divisor = 0.000782*attackspeed;
//First Speed control
divisor /= overallscale;
Float64 remainder = divisor;
divisor = 1.0 - divisor;
Float64 divisorB = 0.000819*attackspeed;
//Second Speed control
divisorB /= overallscale;
Float64 remainderB = divisorB;
divisorB = 1.0 - divisorB;
Float64 divisorC = 0.000857;
//Third Speed control
divisorC /= overallscale;
Float64 remainderC = divisorC*attackspeed;
divisorC = 1.0 - divisorC;
//end ButterComp
Float64 dynamicDivisor;
Float64 dynamicRemainder;
//used for variable release
//begin Desk Power Sag
Float64 intensity = 0.0445556;
Float64 depthA = 2.42;
int offsetA = (int)(depthA * overallscale);
if (offsetA < 1) offsetA = 1;
if (offsetA > 498) offsetA = 498;
Float64 depthB = 2.42; //was 3.38
int offsetB = (int)(depthB * overallscale);
if (offsetB < 1) offsetB = 1;
if (offsetB > 498) offsetB = 498;
Float64 depthC = 2.42; //was 4.35
int offsetC = (int)(depthC * overallscale);
if (offsetC < 1) offsetC = 1;
if (offsetC > 498) offsetC = 498;
Float64 clamp;
Float64 thickness;
Float64 out;
Float64 bridgerectifier;
Float64 powerSag = 0.003300223685324102874217; //was .00365
//the Power Sag constant is how much the sag is cut back in high compressions. Increasing it
//increases the guts and gnarl of the music, decreasing it or making it negative causes the texture to go
//'ethereal' and unsolid under compression. Very subtle!
//end Desk Power Sag
Float64 ratio = sqrt(GetParameter( kParam_Two ))-1.0;
if (ratio > 2.99999) ratio = 2.99999;
if (ratio < 0.0) ratio = 0.0;
//anything we do must adapt to our dry/a/b/c output stages
int ratioselector = floor( ratio );
//zero to three, it'll become, always with 3 as the max
ratio -= ratioselector;
Float64 invRatio = 1.0 - ratio;
//for all processing we've settled on the 'stage' and are just interpolating between top and bottom
//ratio is the more extreme case, invratio becomes our 'floor' case including drySample
Float64 outSampleAL = 0.0;
Float64 outSampleBL = 0.0;
Float64 outSampleCL = 0.0;
Float64 outSampleAR = 0.0;
Float64 outSampleBR = 0.0;
Float64 outSampleCR = 0.0;
//what we interpolate between using ratio
Float64 outputgain = pow(10.0,GetParameter( kParam_Four )/20.0);
Float64 wet = GetParameter( kParam_Five );
Float64 dry = 1.0 - wet;
while (nSampleFrames-- > 0) {
inputSampleL = *inputL;
inputSampleR = *inputR;
//assign working variables like the dry
if (inputSampleL<1.2e-38 && -inputSampleL<1.2e-38) {
static int noisesource = 0;
//this declares a variable before anything else is compiled. It won't keep assigning
//it to 0 for every sample, it's as if the declaration doesn't exist in this context,
//but it lets me add this denormalization fix in a single place rather than updating
//it in three different locations. The variable isn't thread-safe but this is only
//a random seed and we can share it with whatever.
noisesource = noisesource % 1700021; noisesource++;
int residue = noisesource * noisesource;
residue = residue % 170003; residue *= residue;
residue = residue % 17011; residue *= residue;
residue = residue % 1709; residue *= residue;
residue = residue % 173; residue *= residue;
residue = residue % 17;
double applyresidue = residue;
applyresidue *= 0.00000001;
applyresidue *= 0.00000001;
inputSampleL = applyresidue;
}
if (inputSampleR<1.2e-38 && -inputSampleR<1.2e-38) {
static int noisesource = 0;
noisesource = noisesource % 1700021; noisesource++;
int residue = noisesource * noisesource;
residue = residue % 170003; residue *= residue;
residue = residue % 17011; residue *= residue;
residue = residue % 1709; residue *= residue;
residue = residue % 173; residue *= residue;
residue = residue % 17;
double applyresidue = residue;
applyresidue *= 0.00000001;
applyresidue *= 0.00000001;
inputSampleR = applyresidue;
//this denormalization routine produces a white noise at -300 dB which the noise
//shaping will interact with to produce a bipolar output, but the noise is actually
//all positive. That should stop any variables from going denormal, and the routine
//only kicks in if digital black is input. As a final touch, if you save to 24-bit
//the silence will return to being digital black again.
}
drySampleL = inputSampleL;
drySampleR = inputSampleR;
gcount--;
if (gcount < 0 || gcount > 499) {gcount = 499;}
//begin first Power SagL
dL[gcount+499] = dL[gcount] = fabs(inputSampleL)*(intensity-((controlAposL+controlBposL+controlAnegL+controlBnegL)*powerSag));
controlL += (dL[gcount] / offsetA);
controlL -= (dL[gcount+offsetA] / offsetA);
controlL -= 0.000001;
clamp = 1;
if (controlL < 0) {controlL = 0;}
if (controlL > 1) {clamp -= (controlL - 1); controlL = 1;}
if (clamp < 0.5) {clamp = 0.5;}
//control = 0 to 1
thickness = ((1.0 - controlL) * 2.0) - 1.0;
out = fabs(thickness);
bridgerectifier = fabs(inputSampleL);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (thickness > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1-cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleL > 0) inputSampleL = (inputSampleL*(1-out))+(bridgerectifier*out);
else inputSampleL = (inputSampleL*(1-out))-(bridgerectifier*out);
//blend according to density control
if (clamp != 1.0) inputSampleL *= clamp;
//end first Power SagL
//begin first Power SagR
dR[gcount+499] = dR[gcount] = fabs(inputSampleR)*(intensity-((controlAposR+controlBposR+controlAnegR+controlBnegR)*powerSag));
controlR += (dR[gcount] / offsetA);
controlR -= (dR[gcount+offsetA] / offsetA);
controlR -= 0.000001;
clamp = 1;
if (controlR < 0) {controlR = 0;}
if (controlR > 1) {clamp -= (controlR - 1); controlR = 1;}
if (clamp < 0.5) {clamp = 0.5;}
//control = 0 to 1
thickness = ((1.0 - controlR) * 2.0) - 1.0;
out = fabs(thickness);
bridgerectifier = fabs(inputSampleR);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (thickness > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1-cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleR > 0) inputSampleR = (inputSampleR*(1-out))+(bridgerectifier*out);
else inputSampleR = (inputSampleR*(1-out))-(bridgerectifier*out);
//blend according to density control
if (clamp != 1.0) inputSampleR *= clamp;
//end first Power SagR
//begin first compressorL
if (inputgain != 1.0) inputSampleL *= inputgain;
inputpos = (inputSampleL * fpOld) + (avgAL * fpNew) + 1.0;
avgAL = inputSampleL;
//hovers around 1 when there's no signal
if (inputpos < 0.001) inputpos = 0.001;
outputpos = inputpos / 2.0;
if (outputpos > 1.0) outputpos = 1.0;
inputpos *= inputpos;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainder * (inputpos + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetposL *= dynamicDivisor;
targetposL += (inputpos * dynamicRemainder);
calcpos = pow((1.0/targetposL),2);
//extra tiny, quick, if the inputpos of this polarity is high
inputneg = (-inputSampleL * fpOld) + (nvgAL * fpNew) + 1.0;
nvgAL = -inputSampleL;
if (inputneg < 0.001) inputneg = 0.001;
outputneg = inputneg / 2.0;
if (outputneg > 1.0) outputneg = 1.0;
inputneg *= inputneg;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainder * (inputneg + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetnegL *= dynamicDivisor;
targetnegL += (inputneg * dynamicRemainder);
calcneg = pow((1.0/targetnegL),2);
//now we have mirrored targets for comp
//each calc is a blowed up chased target from tiny (at high levels of that polarity) to 1 at no input
//calc is the one we want to react differently: go tiny quick,
//outputpos and outputneg go from 0 to 1
if (inputSampleL > 0)
{ //working on pos
if (true == fpFlip)
{
controlAposL *= divisor;
controlAposL += (calcpos*remainder);
if (controlAposR > controlAposL) controlAposR = (controlAposR + controlAposL) * 0.5;
//this part makes the compressor linked: both channels will converge towards whichever
//is the most compressed at the time.
}
else
{
controlBposL *= divisor;
controlBposL += (calcpos*remainder);
if (controlBposR > controlBposL) controlBposR = (controlBposR + controlBposL) * 0.5;
}
}
else
{ //working on neg
if (true == fpFlip)
{
controlAnegL *= divisor;
controlAnegL += (calcneg*remainder);
if (controlAnegR > controlAnegL) controlAnegR = (controlAnegR + controlAnegL) * 0.5;
}
else
{
controlBnegL *= divisor;
controlBnegL += (calcneg*remainder);
if (controlBnegR > controlBnegL) controlBnegR = (controlBnegR + controlBnegL) * 0.5;
}
}
//this causes each of the four to update only when active and in the correct 'fpFlip'
if (true == fpFlip)
{totalmultiplier = (controlAposL * outputpos) + (controlAnegL * outputneg);}
else
{totalmultiplier = (controlBposL * outputpos) + (controlBnegL * outputneg);}
//this combines the sides according to fpFlip, blending relative to the input value
if (totalmultiplier != 1.0) inputSampleL *= totalmultiplier;
//if (compoutgain != 1.0) inputSample /= compoutgain;
if (inputSampleL > 36.0) inputSampleL = 36.0;
if (inputSampleL < -36.0) inputSampleL = -36.0;
//build in +18db hard clip on insano inputs
outSampleAL = inputSampleL / compoutgain;
//end first compressorL
//begin first compressorR
if (inputgain != 1.0) inputSampleR *= inputgain;
inputpos = (inputSampleR * fpOld) + (avgAR * fpNew) + 1.0;
avgAR = inputSampleR;
//hovers around 1 when there's no signal
if (inputpos < 0.001) inputpos = 0.001;
outputpos = inputpos / 2.0;
if (outputpos > 1.0) outputpos = 1.0;
inputpos *= inputpos;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainder * (inputpos + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetposR *= dynamicDivisor;
targetposR += (inputpos * dynamicRemainder);
calcpos = pow((1.0/targetposR),2);
//extra tiny, quick, if the inputpos of this polarity is high
inputneg = (-inputSampleR * fpOld) + (nvgAR * fpNew) + 1.0;
nvgAR = -inputSampleR;
if (inputneg < 0.001) inputneg = 0.001;
outputneg = inputneg / 2.0;
if (outputneg > 1.0) outputneg = 1.0;
inputneg *= inputneg;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainder * (inputneg + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetnegR *= dynamicDivisor;
targetnegR += (inputneg * dynamicRemainder);
calcneg = pow((1.0/targetnegR),2);
//now we have mirrored targets for comp
//each calc is a blowed up chased target from tiny (at high levels of that polarity) to 1 at no input
//calc is the one we want to react differently: go tiny quick,
//outputpos and outputneg go from 0 to 1
if (inputSampleR > 0)
{ //working on pos
if (true == fpFlip)
{
controlAposR *= divisor;
controlAposR += (calcpos*remainder);
if (controlAposL > controlAposR) controlAposL = (controlAposR + controlAposL) * 0.5;
//this part makes the compressor linked: both channels will converge towards whichever
//is the most compressed at the time.
}
else
{
controlBposR *= divisor;
controlBposR += (calcpos*remainder);
if (controlBposL > controlBposR) controlBposL = (controlBposR + controlBposL) * 0.5;
}
}
else
{ //working on neg
if (true == fpFlip)
{
controlAnegR *= divisor;
controlAnegR += (calcneg*remainder);
if (controlAnegL > controlAnegR) controlAnegL = (controlAnegR + controlAnegL) * 0.5;
}
else
{
controlBnegR *= divisor;
controlBnegR += (calcneg*remainder);
if (controlBnegL > controlBnegR) controlBnegL = (controlBnegR + controlBnegL) * 0.5;
}
}
//this causes each of the four to update only when active and in the correct 'fpFlip'
if (true == fpFlip)
{totalmultiplier = (controlAposR * outputpos) + (controlAnegR * outputneg);}
else
{totalmultiplier = (controlBposR * outputpos) + (controlBnegR * outputneg);}
//this combines the sides according to fpFlip, blending relative to the input value
if (totalmultiplier != 1.0) inputSampleR *= totalmultiplier;
//if (compoutgain != 1.0) inputSample /= compoutgain;
if (inputSampleR > 36.0) inputSampleR = 36.0;
if (inputSampleR < -36.0) inputSampleR = -36.0;
//build in +18db hard clip on insano inputs
outSampleAR = inputSampleR / compoutgain;
//end first compressorR
if (ratioselector > 0) {
//begin second Power SagL
bL[gcount+499] = bL[gcount] = fabs(inputSampleL)*(intensity-((controlAposBL+controlBposBL+controlAnegBL+controlBnegBL)*powerSag));
controlBL += (bL[gcount] / offsetB);
controlBL -= (bL[gcount+offsetB] / offsetB);
controlBL -= 0.000001;
clamp = 1;
if (controlBL < 0) {controlBL = 0;}
if (controlBL > 1) {clamp -= (controlBL - 1); controlBL = 1;}
if (clamp < 0.5) {clamp = 0.5;}
//control = 0 to 1
thickness = ((1.0 - controlBL) * 2.0) - 1.0;
out = fabs(thickness);
bridgerectifier = fabs(inputSampleL);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (thickness > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1-cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleL > 0) inputSampleL = (inputSampleL*(1-out))+(bridgerectifier*out);
else inputSampleL = (inputSampleL*(1-out))-(bridgerectifier*out);
//blend according to density control
if (clamp != 1.0) inputSampleL *= clamp;
//end second Power SagL
//begin second Power SagR
bR[gcount+499] = bR[gcount] = fabs(inputSampleR)*(intensity-((controlAposBR+controlBposBR+controlAnegBR+controlBnegBR)*powerSag));
controlBR += (bR[gcount] / offsetB);
controlBR -= (bR[gcount+offsetB] / offsetB);
controlBR -= 0.000001;
clamp = 1;
if (controlBR < 0) {controlBR = 0;}
if (controlBR > 1) {clamp -= (controlBR - 1); controlBR = 1;}
if (clamp < 0.5) {clamp = 0.5;}
//control = 0 to 1
thickness = ((1.0 - controlBR) * 2.0) - 1.0;
out = fabs(thickness);
bridgerectifier = fabs(inputSampleR);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (thickness > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1-cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleR > 0) inputSampleR = (inputSampleR*(1-out))+(bridgerectifier*out);
else inputSampleR = (inputSampleR*(1-out))-(bridgerectifier*out);
//blend according to density control
if (clamp != 1.0) inputSampleR *= clamp;
//end second Power SagR
//begin second compressorL
inputpos = (inputSampleL * fpOld) + (avgBL * fpNew) + 1.0;
avgBL = inputSampleL;
if (inputpos < 0.001) inputpos = 0.001;
outputpos = inputpos / 2.0;
if (outputpos > 1.0) outputpos = 1.0;
inputpos *= inputpos;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderB * (inputpos + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetposBL *= dynamicDivisor;
targetposBL += (inputpos * dynamicRemainder);
calcpos = pow((1.0/targetposBL),2);
inputneg = (-inputSampleL * fpOld) + (nvgBL * fpNew) + 1.0;
nvgBL = -inputSampleL;
if (inputneg < 0.001) inputneg = 0.001;
outputneg = inputneg / 2.0;
if (outputneg > 1.0) outputneg = 1.0;
inputneg *= inputneg;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderB * (inputneg + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetnegBL *= dynamicDivisor;
targetnegBL += (inputneg * dynamicRemainder);
calcneg = pow((1.0/targetnegBL),2);
//now we have mirrored targets for comp
//outputpos and outputneg go from 0 to 1
if (inputSampleL > 0)
{ //working on pos
if (true == fpFlip)
{
controlAposBL *= divisorB;
controlAposBL += (calcpos*remainderB);
if (controlAposBR > controlAposBL) controlAposBR = (controlAposBR + controlAposBL) * 0.5;
//this part makes the compressor linked: both channels will converge towards whichever
//is the most compressed at the time.
}
else
{
controlBposBL *= divisorB;
controlBposBL += (calcpos*remainderB);
if (controlBposBR > controlBposBL) controlBposBR = (controlBposBR + controlBposBL) * 0.5;
}
}
else
{ //working on neg
if (true == fpFlip)
{
controlAnegBL *= divisorB;
controlAnegBL += (calcneg*remainderB);
if (controlAnegBR > controlAnegBL) controlAnegBR = (controlAnegBR + controlAnegBL) * 0.5;
}
else
{
controlBnegBL *= divisorB;
controlBnegBL += (calcneg*remainderB);
if (controlBnegBR > controlBnegBL) controlBnegBR = (controlBnegBR + controlBnegBL) * 0.5;
}
}
//this causes each of the four to update only when active and in the correct 'fpFlip'
if (true == fpFlip)
{totalmultiplier = (controlAposBL * outputpos) + (controlAnegBL * outputneg);}
else
{totalmultiplier = (controlBposBL * outputpos) + (controlBnegBL * outputneg);}
//this combines the sides according to fpFlip, blending relative to the input value
if (totalmultiplier != 1.0) inputSampleL *= totalmultiplier;
//if (compoutgain != 1.0) inputSample /= compoutgain;
if (inputSampleL > 36.0) inputSampleL = 36.0;
if (inputSampleL < -36.0) inputSampleL = -36.0;
//build in +18db hard clip on insano inputs
outSampleBL = inputSampleL / compoutgain;
//end second compressorL
//begin second compressorR
inputpos = (inputSampleR * fpOld) + (avgBR * fpNew) + 1.0;
avgBR = inputSampleR;
if (inputpos < 0.001) inputpos = 0.001;
outputpos = inputpos / 2.0;
if (outputpos > 1.0) outputpos = 1.0;
inputpos *= inputpos;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderB * (inputpos + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetposBR *= dynamicDivisor;
targetposBR += (inputpos * dynamicRemainder);
calcpos = pow((1.0/targetposBR),2);
inputneg = (-inputSampleR * fpOld) + (nvgBR * fpNew) + 1.0;
nvgBR = -inputSampleR;
if (inputneg < 0.001) inputneg = 0.001;
outputneg = inputneg / 2.0;
if (outputneg > 1.0) outputneg = 1.0;
inputneg *= inputneg;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderB * (inputneg + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetnegBR *= dynamicDivisor;
targetnegBR += (inputneg * dynamicRemainder);
calcneg = pow((1.0/targetnegBR),2);
//now we have mirrored targets for comp
//outputpos and outputneg go from 0 to 1
if (inputSampleR > 0)
{ //working on pos
if (true == fpFlip)
{
controlAposBR *= divisorB;
controlAposBR += (calcpos*remainderB);
if (controlAposBL > controlAposBR) controlAposBL = (controlAposBR + controlAposBL) * 0.5;
//this part makes the compressor linked: both channels will converge towards whichever
//is the most compressed at the time.
}
else
{
controlBposBR *= divisorB;
controlBposBR += (calcpos*remainderB);
if (controlBposBL > controlBposBR) controlBposBL = (controlBposBR + controlBposBL) * 0.5;
}
}
else
{ //working on neg
if (true == fpFlip)
{
controlAnegBR *= divisorB;
controlAnegBR += (calcneg*remainderB);
if (controlAnegBL > controlAnegBR) controlAnegBL = (controlAnegBR + controlAnegBL) * 0.5;
}
else
{
controlBnegBR *= divisorB;
controlBnegBR += (calcneg*remainderB);
if (controlBnegBL > controlBnegBR) controlBnegBL = (controlBnegBR + controlBnegBL) * 0.5;
}
}
//this causes each of the four to update only when active and in the correct 'fpFlip'
if (true == fpFlip)
{totalmultiplier = (controlAposBR * outputpos) + (controlAnegBR * outputneg);}
else
{totalmultiplier = (controlBposBR * outputpos) + (controlBnegBR * outputneg);}
//this combines the sides according to fpFlip, blending relative to the input value
if (totalmultiplier != 1.0) inputSampleR *= totalmultiplier;
//if (compoutgain != 1.0) inputSample /= compoutgain;
if (inputSampleR > 36.0) inputSampleR = 36.0;
if (inputSampleR < -36.0) inputSampleR = -36.0;
//build in +18db hard clip on insano inputs
outSampleBR = inputSampleR / compoutgain;
//end second compressorR
if (ratioselector > 1) {
//begin third Power SagL
cL[gcount+499] = cL[gcount] = fabs(inputSampleL)*(intensity-((controlAposCL+controlBposCL+controlAnegCL+controlBnegCL)*powerSag));
controlCL += (cL[gcount] / offsetC);
controlCL -= (cL[gcount+offsetB] / offsetC);
controlCL -= 0.000001;
clamp = 1;
if (controlCL < 0) {controlCL = 0;}
if (controlCL > 1) {clamp -= (controlCL - 1); controlCL = 1;}
if (clamp < 0.5) {clamp = 0.5;}
//control = 0 to 1
thickness = ((1.0 - controlCL) * 2.0) - 1.0;
out = fabs(thickness);
bridgerectifier = fabs(inputSampleL);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (thickness > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1-cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleL > 0) inputSampleL = (inputSampleL*(1-out))+(bridgerectifier*out);
else inputSampleL = (inputSampleL*(1-out))-(bridgerectifier*out);
//blend according to density control
if (clamp != 1.0) inputSampleL *= clamp;
//end third Power SagL
//begin third Power SagR
cR[gcount+499] = cR[gcount] = fabs(inputSampleR)*(intensity-((controlAposCR+controlBposCR+controlAnegCR+controlBnegCR)*powerSag));
controlCR += (cR[gcount] / offsetC);
controlCR -= (cR[gcount+offsetB] / offsetC);
controlCR -= 0.000001;
clamp = 1;
if (controlCR < 0) {controlCR = 0;}
if (controlCR > 1) {clamp -= (controlCR - 1); controlCR = 1;}
if (clamp < 0.5) {clamp = 0.5;}
//control = 0 to 1
thickness = ((1.0 - controlCR) * 2.0) - 1.0;
out = fabs(thickness);
bridgerectifier = fabs(inputSampleR);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (thickness > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1-cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleR > 0) inputSampleR = (inputSampleR*(1-out))+(bridgerectifier*out);
else inputSampleR = (inputSampleR*(1-out))-(bridgerectifier*out);
//blend according to density control
if (clamp != 1.0) inputSampleR *= clamp;
//end third Power SagR
//begin third compressorL
inputpos = (inputSampleL * fpOld) + (avgCL * fpNew) + 1.0;
avgCL = inputSampleL;
if (inputpos < 0.001) inputpos = 0.001;
outputpos = inputpos / 2.0;
if (outputpos > 1.0) outputpos = 1.0;
inputpos *= inputpos;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderC * (inputpos + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetposCL *= dynamicDivisor;
targetposCL += (inputpos * dynamicRemainder);
calcpos = pow((1.0/targetposCL),2);
inputneg = (-inputSampleL * fpOld) + (nvgCL * fpNew) + 1.0;
nvgCL = -inputSampleL;
if (inputneg < 0.001) inputneg = 0.001;
outputneg = inputneg / 2.0;
if (outputneg > 1.0) outputneg = 1.0;
inputneg *= inputneg;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderC * (inputneg + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetnegCL *= dynamicDivisor;
targetnegCL += (inputneg * dynamicRemainder);
calcneg = pow((1.0/targetnegCL),2);
//now we have mirrored targets for comp
//outputpos and outputneg go from 0 to 1
if (inputSampleL > 0)
{ //working on pos
if (true == fpFlip)
{
controlAposCL *= divisorC;
controlAposCL += (calcpos*remainderC);
if (controlAposCR > controlAposCL) controlAposCR = (controlAposCR + controlAposCL) * 0.5;
//this part makes the compressor linked: both channels will converge towards whichever
//is the most compressed at the time.
}
else
{
controlBposCL *= divisorC;
controlBposCL += (calcpos*remainderC);
if (controlBposCR > controlBposCL) controlBposCR = (controlBposCR + controlBposCL) * 0.5;
}
}
else
{ //working on neg
if (true == fpFlip)
{
controlAnegCL *= divisorC;
controlAnegCL += (calcneg*remainderC);
if (controlAnegCR > controlAnegCL) controlAnegCR = (controlAnegCR + controlAnegCL) * 0.5;
}
else
{
controlBnegCL *= divisorC;
controlBnegCL += (calcneg*remainderC);
if (controlBnegCR > controlBnegCL) controlBnegCR = (controlBnegCR + controlBnegCL) * 0.5;
}
}
//this causes each of the four to update only when active and in the correct 'fpFlip'
if (true == fpFlip)
{totalmultiplier = (controlAposCL * outputpos) + (controlAnegCL * outputneg);}
else
{totalmultiplier = (controlBposCL * outputpos) + (controlBnegCL * outputneg);}
//this combines the sides according to fpFlip, blending relative to the input value
if (totalmultiplier != 1.0) inputSampleL *= totalmultiplier;
if (inputSampleL > 36.0) inputSampleL = 36.0;
if (inputSampleL < -36.0) inputSampleL = -36.0;
//build in +18db hard clip on insano inputs
outSampleCL = inputSampleL / compoutgain;
//if (compoutgain != 1.0) inputSample /= compoutgain;
//end third compressorL
//begin third compressorR
inputpos = (inputSampleR * fpOld) + (avgCR * fpNew) + 1.0;
avgCR = inputSampleR;
if (inputpos < 0.001) inputpos = 0.001;
outputpos = inputpos / 2.0;
if (outputpos > 1.0) outputpos = 1.0;
inputpos *= inputpos;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderC * (inputpos + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetposCL *= dynamicDivisor;
targetposCL += (inputpos * dynamicRemainder);
calcpos = pow((1.0/targetposCR),2);
inputneg = (-inputSampleR * fpOld) + (nvgCR * fpNew) + 1.0;
nvgCR = -inputSampleR;
if (inputneg < 0.001) inputneg = 0.001;
outputneg = inputneg / 2.0;
if (outputneg > 1.0) outputneg = 1.0;
inputneg *= inputneg;
//will be very high for hot input, can be 0.00001-1 for other-polarity
dynamicRemainder = remainderC * (inputneg + 1.0);
if (dynamicRemainder > 1.0) dynamicRemainder = 1.0;
dynamicDivisor = 1.0 - dynamicRemainder;
//calc chases much faster if input swing is high
targetnegCR *= dynamicDivisor;
targetnegCR += (inputneg * dynamicRemainder);
calcneg = pow((1.0/targetnegCR),2);
//now we have mirrored targets for comp
//outputpos and outputneg go from 0 to 1
if (inputSampleR > 0)
{ //working on pos
if (true == fpFlip)
{
controlAposCR *= divisorC;
controlAposCR += (calcpos*remainderC);
if (controlAposCL > controlAposCR) controlAposCL = (controlAposCR + controlAposCL) * 0.5;
//this part makes the compressor linked: both channels will converge towards whichever
//is the most compressed at the time.
}
else
{
controlBposCR *= divisorC;
controlBposCR += (calcpos*remainderC);
if (controlBposCL > controlBposCR) controlBposCL = (controlBposCR + controlBposCL) * 0.5;
}
}
else
{ //working on neg
if (true == fpFlip)
{
controlAnegCR *= divisorC;
controlAnegCR += (calcneg*remainderC);
if (controlAnegCL > controlAnegCR) controlAnegCL = (controlAnegCR + controlAnegCL) * 0.5;
}
else
{
controlBnegCR *= divisorC;
controlBnegCR += (calcneg*remainderC);
if (controlBnegCL > controlBnegCR) controlBnegCL = (controlBnegCR + controlBnegCL) * 0.5;
}
}
//this causes each of the four to update only when active and in the correct 'fpFlip'
if (true == fpFlip)
{totalmultiplier = (controlAposCR * outputpos) + (controlAnegCR * outputneg);}
else
{totalmultiplier = (controlBposCR * outputpos) + (controlBnegCR * outputneg);}
//this combines the sides according to fpFlip, blending relative to the input value
if (totalmultiplier != 1.0) inputSampleR *= totalmultiplier;
if (inputSampleR > 36.0) inputSampleR = 36.0;
if (inputSampleR < -36.0) inputSampleR = -36.0;
//build in +18db hard clip on insano inputs
outSampleCR = inputSampleR / compoutgain;
//if (compoutgain != 1.0) inputSample /= compoutgain;
//end third compressorR
}
} //these nested if blocks are not indented because the old xCode doesn't support it
//here we will interpolate between dry, and the three post-stages of processing
switch (ratioselector) {
case 0:
inputSampleL = (drySampleL * invRatio) + (outSampleAL * ratio);
inputSampleR = (drySampleR * invRatio) + (outSampleAR * ratio);
break;
case 1:
inputSampleL = (outSampleAL * invRatio) + (outSampleBL * ratio);
inputSampleR = (outSampleAR * invRatio) + (outSampleBR * ratio);
break;
default:
inputSampleL = (outSampleBL * invRatio) + (outSampleCL * ratio);
inputSampleR = (outSampleBR * invRatio) + (outSampleCR * ratio);
break;
}
//only then do we reconstruct the output, but our ratio is built here
if (outputgain != 1.0) {
inputSampleL *= outputgain;
inputSampleR *= outputgain;
}
if (wet != 1.0) {
inputSampleL = (inputSampleL * wet) + (drySampleL * dry);
inputSampleR = (inputSampleR * wet) + (drySampleR * dry);
}
fpFlip = !fpFlip;
//stereo 32 bit dither, made small and tidy.
int expon; frexpf((Float32)inputSampleL, &expon);
long double dither = (rand()/(RAND_MAX*7.737125245533627e+25))*pow(2,expon+62);
inputSampleL += (dither-fpNShapeL); fpNShapeL = dither;
frexpf((Float32)inputSampleR, &expon);
dither = (rand()/(RAND_MAX*7.737125245533627e+25))*pow(2,expon+62);
inputSampleR += (dither-fpNShapeR); fpNShapeR = dither;
//end 32 bit dither
*outputL = inputSampleL;
*outputR = inputSampleR;
inputL += 1; inputR += 1; outputL += 1; outputR += 1;
}
return noErr;
}