aboutsummaryrefslogblamecommitdiffstats
path: root/plugins/MacAU/Ditherbox/Ditherbox.cpp
blob: a4b1edfc40959495fa70aaf017556b24a5f65f08 (plain) (tree)




















































































                                                                                                                                          


                                                     







                                                     


                                                       














































































































































































































                                                                                                                                                    
                                                     
        
                                                            


                                                                     


























                                                                                                             





















                                                                            























































                                                                                                      


















                                                                   
                       
















































                                                                      
                                




















                                                                                                                                                       
                                














































































                                                                                                                        
                                                                          
















































































                                                                                                                        
                                 







                                                                       
                         











































































































                                                                                                                    
                         










































                                                                                       
/*
*	File:		Ditherbox.cpp
*	
*	Version:	1.0
* 
*	Created:	1/1/09
*	
*	Copyright:  Copyright � 2009 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.
*
*/
/*=============================================================================
	Ditherbox.h
	
=============================================================================*/
#include "Ditherbox.h"


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

COMPONENT_ENTRY(Ditherbox)


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::Ditherbox
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Ditherbox::Ditherbox(AudioUnit component)
	: AUEffectBase(component)
{
	CreateElements();
	Globals()->UseIndexedParameters(kNumberOfParameters);
	SetParameter(kParam_One, kDefaultValue_ParamOne );
        
#if AU_DEBUG_DISPATCHER
	mDebugDispatcher = new AUDebugDispatcher (this);
#endif
	
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::GetParameterValueStrings
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult		Ditherbox::GetParameterValueStrings(AudioUnitScope		inScope,
                                                                AudioUnitParameterID	inParameterID,
                                                                CFArrayRef *		outStrings)
{
    if ((inScope == kAudioUnitScope_Global) && (inParameterID == kParam_One)) //ID must be actual name of parameter identifier, not number
	{
			if (outStrings == NULL) return noErr;
			CFStringRef strings [] =
			{
				kMenuItem_Truncate,
				kMenuItem_Flat,
				kMenuItem_TPDF,
				kMenuItem_Paul,
				kMenuItem_DoublePaul,
				kMenuItem_Tape,
				kMenuItem_Quadratic,
				kMenuItem_TenNines,
				kMenuItem_Contingent,
				kMenuItem_Naturalize,
				kMenuItem_NJAD,
				kMenuItem_TruncateHR,
				kMenuItem_FlatHR,
				kMenuItem_TPDFHR,
				kMenuItem_PaulHR,
				kMenuItem_DoublePaulHR,
				kMenuItem_TapeHR,
				kMenuItem_QuadraticHR,
				kMenuItem_TenNinesHR,
				kMenuItem_ContingentHR,
				kMenuItem_NaturalizeHR,
				kMenuItem_NJADHR,
				kMenuItem_SlewOnly,
				kMenuItem_SubsOnly,
				kMenuItem_Silhouette,
			};
			*outStrings = CFArrayCreate (
			NULL,
			(const void **) strings,
			(sizeof (strings) / sizeof (strings [0])),
			NULL
		);
		return noErr;
	}
    return kAudioUnitErr_InvalidProperty;
}



//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::GetParameterInfo
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult		Ditherbox::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_Indexed;
				outParameterInfo.minValue = kTruncate;
				outParameterInfo.maxValue = kSilhouette;
				outParameterInfo.defaultValue = kDefaultValue_ParamOne;
				break;
			 default:
                result = kAudioUnitErr_InvalidParameter;
                break;
            }
	} else {
        result = kAudioUnitErr_InvalidParameter;
    }
    


	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::GetPropertyInfo
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult		Ditherbox::GetPropertyInfo (AudioUnitPropertyID	inID,
                                                        AudioUnitScope		inScope,
                                                        AudioUnitElement	inElement,
                                                        UInt32 &		outDataSize,
                                                        Boolean &		outWritable)
{
	return AUEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::GetProperty
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult		Ditherbox::GetProperty(	AudioUnitPropertyID inID,
                                                        AudioUnitScope 		inScope,
                                                        AudioUnitElement 	inElement,
                                                        void *			outData )
{
	return AUEffectBase::GetProperty (inID, inScope, inElement, outData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::Initialize
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ComponentResult Ditherbox::Initialize()
{
    ComponentResult result = AUEffectBase::Initialize();
    if (result == noErr)
        Reset(kAudioUnitScope_Global, 0);
    return result;
}

#pragma mark ____DitherboxEffectKernel


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::DitherboxKernel::Reset()
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		Ditherbox::DitherboxKernel::Reset()
{
	Position = 99999999;
	contingentErr = 0.0;
	byn[0] = 1000;
	byn[1] = 301;
	byn[2] = 176;
	byn[3] = 125;
	byn[4] = 97;
	byn[5] = 79;
	byn[6] = 67;
	byn[7] = 58;
	byn[8] = 51;
	byn[9] = 46;
	byn[10] = 1000;
	noiseShaping = 0.0;
	NSOdd = 0.0;
	NSEven = 0.0;
	prev = 0.0;
	ns[0] = 0;
	ns[1] = 0;
	ns[2] = 0;
	ns[3] = 0;
	ns[4] = 0;
	ns[5] = 0;
	ns[6] = 0;
	ns[7] = 0;
	ns[8] = 0;
	ns[9] = 0;
	ns[10] = 0;
	ns[11] = 0;
	ns[12] = 0;
	ns[13] = 0;
	ns[14] = 0;
	ns[15] = 0;
	lastSample = 0.0;
	outSample = 0.0;
	iirSampleA = 0.0;
	iirSampleB = 0.0;
	iirSampleC = 0.0;
	iirSampleD = 0.0;
	iirSampleE = 0.0;
	iirSampleF = 0.0;
	iirSampleG = 0.0;
	iirSampleH = 0.0;
	iirSampleI = 0.0;
	iirSampleJ = 0.0;
	iirSampleK = 0.0;
	iirSampleL = 0.0;
	iirSampleM = 0.0;
	iirSampleN = 0.0;
	iirSampleO = 0.0;
	iirSampleP = 0.0;
	iirSampleQ = 0.0;
	iirSampleR = 0.0;
	iirSampleS = 0.0;
	iirSampleT = 0.0;
	iirSampleU = 0.0;
	iirSampleV = 0.0;
	iirSampleW = 0.0;
	iirSampleX = 0.0;
	iirSampleY = 0.0;
	iirSampleZ = 0.0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	Ditherbox::DitherboxKernel::Process
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void		Ditherbox::DitherboxKernel::Process(	const Float32 	*inSourceP,
                                                    Float32		 	*inDestP,
                                                    UInt32 			inFramesToProcess,
                                                    UInt32			inNumChannels, // for version 2 AudioUnits inNumChannels is always 1
                                                    bool			&ioSilence )
{

	
	UInt32 nSampleFrames = inFramesToProcess;
	const Float32 *sourceP = inSourceP;
	Float32 *destP = inDestP;
	long double contingentRnd;
	long double absSample;
	long double contingent;
	long double overallscale = 1.0;
	overallscale /= 44100.0;
	overallscale *= GetSampleRate();
	long double iirAmount = 2250/44100.0;
	long double gaintarget = 1.42;
	long double gain;
	iirAmount /= overallscale;
	long double altAmount = 1.0 - iirAmount;
	long double inputSample;
	long double outputSample;
	long double silhouette;
	long double smoother;
	long double bridgerectifier;
	long double benfordize;
	int hotbinA;
	int hotbinB;
	long double totalA;
	long double totalB;
	long double randyConstant = 1.61803398874989484820458683436563811772030917980576;
	long double omegaConstant = 0.56714329040978387299996866221035554975381578718651;
	long double expConstant = 0.06598803584531253707679018759684642493857704825279;
	long double trim = 2.302585092994045684017991; //natural logarithm of 10
	int dtype = (int) GetParameter( kParam_One ); // +1 for Reaper bug workaround
	bool highRes = false;
	bool dithering = true;
	
	Float32 drySample; //should be the same as what the native DAW buss is

	if (dtype > 11){highRes = true; dtype -= 11;}
	
	if (dtype > 11){dithering = false; highRes = false;}
	//follow up by switching high res back off for the monitoring
	
	while (nSampleFrames-- > 0) {
		inputSample = *sourceP;
		if (inputSample<1.2e-38 && -inputSample<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;
			inputSample = 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.
		}
		drySample = inputSample;
		
		sourceP += inNumChannels;
		
		if (dtype == 8) inputSample -= noiseShaping;
		
		if (dithering) inputSample *= 32768.0;
		//denormalizing as way of controlling insane detail boosting
		if (highRes) inputSample *= 256.0; //256 for 16/24 version
		
		switch (dtype)
		{
		case 1: 
		inputSample = floor(inputSample);
		//truncate
		break;
		
		case 2: 
		inputSample += (rand()/(double)RAND_MAX);
		inputSample -= 0.5;
		inputSample = floor(inputSample);
		//flat dither
		break;
		
			case 3:
				inputSample += (rand()/(double)RAND_MAX);
				inputSample += (rand()/(double)RAND_MAX);
				inputSample -= 1.0;
				inputSample = floor(inputSample);
				//TPDF dither
				break;
				
			case 4:
				currentDither = (rand()/(double)RAND_MAX);
				inputSample += currentDither;
				inputSample -= lastSample;
				inputSample = floor(inputSample);
				lastSample = currentDither;
				//Paul dither
				break;
				
			case 5:
				ns[9] = ns[8]; ns[8] = ns[7]; ns[7] = ns[6]; ns[6] = ns[5];
				ns[5] = ns[4]; ns[4] = ns[3]; ns[3] = ns[2]; ns[2] = ns[1];
				ns[1] = ns[0]; ns[0] = (rand()/(double)RAND_MAX);
				
				currentDither  = (ns[0] * 0.061);
				currentDither -= (ns[1] * 0.11);
				currentDither += (ns[8] * 0.126);
				currentDither -= (ns[7] * 0.23);
				currentDither += (ns[2] * 0.25);
				currentDither -= (ns[3] * 0.43);
				currentDither += (ns[6] * 0.5);
				currentDither -= ns[5];
				currentDither += ns[4];
				//this sounds different from doing it in order of sample position
				//cumulative tiny errors seem to build up even at this buss depth
				//considerably more pronounced at 32 bit float.
				//Therefore we add the most significant components LAST.
				//trying to keep values on like exponents of the floating point value.
				inputSample += currentDither;
				
				inputSample = floor(inputSample);
				//DoublePaul dither
				break;
				
			case 6:
				currentDither = (rand()/(double)RAND_MAX);
				inputSample += currentDither;
				inputSample -= ns[4];
				inputSample = floor(inputSample);
				ns[4] = ns[3];
				ns[3] = ns[2];
				ns[2] = ns[1];
				ns[1] = currentDither;
				//Tape dither
				break;
				
				
		case 7: 
		Position += 1;
		//Note- uses integer overflow as a 'mod' operator
		hotbinA = Position * Position;
		hotbinA = hotbinA % 170003; //% is C++ mod operator
		hotbinA *= hotbinA;
		hotbinA = hotbinA % 17011; //% is C++ mod operator
		hotbinA *= hotbinA;
		hotbinA = hotbinA % 1709; //% is C++ mod operator
		hotbinA *= hotbinA;
		hotbinA = hotbinA % 173; //% is C++ mod operator
		hotbinA *= hotbinA;
		hotbinA = hotbinA % 17;
		hotbinA *= 0.0635;
		if (flip) hotbinA = -hotbinA;
		inputSample += hotbinA;
		inputSample = floor(inputSample);
		//Quadratic dither
		break;

		case 8:
		absSample = ((rand()/(double)RAND_MAX) - 0.5);
		ns[0] += absSample; ns[0] /= 2; absSample -= ns[0];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[1] += absSample; ns[1] /= 2; absSample -= ns[1];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[2] += absSample; ns[2] /= 2; absSample -= ns[2];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[3] += absSample; ns[3] /= 2; absSample -= ns[3];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[4] += absSample; ns[4] /= 2; absSample -= ns[4];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[5] += absSample; ns[5] /= 2; absSample -= ns[5];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[6] += absSample; ns[6] /= 2; absSample -= ns[6];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[7] += absSample; ns[7] /= 2; absSample -= ns[7];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[8] += absSample; ns[8] /= 2; absSample -= ns[8];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[9] += absSample; ns[9] /= 2; absSample -= ns[9];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[10] += absSample; ns[10] /= 2; absSample -= ns[10];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[11] += absSample; ns[11] /= 2; absSample -= ns[11];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[12] += absSample; ns[12] /= 2; absSample -= ns[12];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[13] += absSample; ns[13] /= 2; absSample -= ns[13];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[14] += absSample; ns[14] /= 2; absSample -= ns[14];
		absSample += ((rand()/(double)RAND_MAX) - 0.5);
		ns[15] += absSample; ns[15] /= 2; absSample -= ns[15];
		//install noise and then shape it
		absSample += inputSample;
		
		//NSOdd /= 1.0001; //NSDensity
		
		if (NSOdd > 0) NSOdd -= 0.97;
		if (NSOdd < 0) NSOdd += 0.97;
		
		NSOdd -= (NSOdd * NSOdd * NSOdd * 0.475);

		NSOdd += prev;
		absSample += (NSOdd*0.475);
		prev = floor(absSample) - inputSample;
		inputSample = floor(absSample);
		//TenNines dither
		break;
		
			case 9: 
				if (inputSample > 0) inputSample += 0.383;
				if (inputSample < 0) inputSample -= 0.383;
				//adjusting to permit more information drug outta the noisefloor
				contingentRnd = (((rand()/(double)RAND_MAX)+(rand()/(double)RAND_MAX))-1.0) * randyConstant; //produce TPDF dist, scale
				contingentRnd -= contingentErr*omegaConstant; //include err
				absSample = fabs(inputSample);
				contingentErr = absSample - floor(absSample); //get next err
				contingent = contingentErr * 2.0; //scale of quantization levels
				if (contingent > 1.0) contingent = ((-contingent+2.0)*omegaConstant) + expConstant;
				else contingent = (contingent * omegaConstant) + expConstant;
				//zero is next to a quantization level, one is exactly between them
				if (flip) contingentRnd = (contingentRnd * (1.0-contingent)) + contingent + 0.5;
				else contingentRnd = (contingentRnd * (1.0-contingent)) - contingent + 0.5;
				inputSample += (contingentRnd * contingent);
				//Contingent Dither
				inputSample = floor(inputSample);
				//note: this does not dither for values exactly the same as 16 bit values-
				//which forces the dither to gate at 0.0. It goes to digital black,
				//and does a teeny parallel-compression thing when almost at digital black.
				break;
				
			case 10:
				if (inputSample > 0) inputSample += (0.3333333333);
				if (inputSample < 0) inputSample -= (0.3333333333);
				
				inputSample += (rand()/(double)RAND_MAX)*0.6666666666;
				
				benfordize = floor(inputSample);
				while (benfordize >= 1.0) {benfordize /= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				hotbinA = floor(benfordize);
				//hotbin becomes the Benford bin value for this number floored
				totalA = 0;
				if ((hotbinA > 0) && (hotbinA < 10))
				{
					byn[hotbinA] += 1;
					totalA += (301-byn[1]);
					totalA += (176-byn[2]);
					totalA += (125-byn[3]);
					totalA += (97-byn[4]);
					totalA += (79-byn[5]);
					totalA += (67-byn[6]);
					totalA += (58-byn[7]);
					totalA += (51-byn[8]);
					totalA += (46-byn[9]);
					byn[hotbinA] -= 1;
				} else {hotbinA = 10;}
				//produce total number- smaller is closer to Benford real
				
				benfordize = ceil(inputSample);
				while (benfordize >= 1.0) {benfordize /= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				hotbinB = floor(benfordize);
				//hotbin becomes the Benford bin value for this number ceiled
				totalB = 0;
				if ((hotbinB > 0) && (hotbinB < 10))
				{
					byn[hotbinB] += 1;
					totalB += (301-byn[1]);
					totalB += (176-byn[2]);
					totalB += (125-byn[3]);
					totalB += (97-byn[4]);
					totalB += (79-byn[5]);
					totalB += (67-byn[6]);
					totalB += (58-byn[7]);
					totalB += (51-byn[8]);
					totalB += (46-byn[9]);
					byn[hotbinB] -= 1;
				} else {hotbinB = 10;}
				//produce total number- smaller is closer to Benford real
				
				if (totalA < totalB)
				{
					byn[hotbinA] += 1;
					inputSample = floor(inputSample);
				}
				else
				{
					byn[hotbinB] += 1;
					inputSample = ceil(inputSample);
				}
				//assign the relevant one to the delay line
				//and floor/ceil signal accordingly
				
				totalA = byn[1] + byn[2] + byn[3] + byn[4] + byn[5] + byn[6] + byn[7] + byn[8] + byn[9];
				totalA /= 1000;
				if (totalA = 0) totalA = 1;
				byn[1] /= totalA;
				byn[2] /= totalA;
				byn[3] /= totalA;
				byn[4] /= totalA;
				byn[5] /= totalA;
				byn[6] /= totalA;
				byn[7] /= totalA;
				byn[8] /= totalA;
				byn[9] /= totalA;
				byn[10] /= 2; //catchall for garbage data
				break;
				
			case 11: //this one is the Not Just Another Dither
				
				benfordize = floor(inputSample);
				while (benfordize >= 1.0) {benfordize /= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				hotbinA = floor(benfordize);
				//hotbin becomes the Benford bin value for this number floored
				totalA = 0;
				if ((hotbinA > 0) && (hotbinA < 10))
				{
					byn[hotbinA] += 1;
					totalA += (301-byn[1]);
					totalA += (176-byn[2]);
					totalA += (125-byn[3]);
					totalA += (97-byn[4]);
					totalA += (79-byn[5]);
					totalA += (67-byn[6]);
					totalA += (58-byn[7]);
					totalA += (51-byn[8]);
					totalA += (46-byn[9]);
					byn[hotbinA] -= 1;
				} else {hotbinA = 10;}
				//produce total number- smaller is closer to Benford real
				
				benfordize = ceil(inputSample);
				while (benfordize >= 1.0) {benfordize /= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				if (benfordize < 1.0) {benfordize *= 10;}
				hotbinB = floor(benfordize);
				//hotbin becomes the Benford bin value for this number ceiled
				totalB = 0;
				if ((hotbinB > 0) && (hotbinB < 10))
				{
					byn[hotbinB] += 1;
					totalB += (301-byn[1]);
					totalB += (176-byn[2]);
					totalB += (125-byn[3]);
					totalB += (97-byn[4]);
					totalB += (79-byn[5]);
					totalB += (67-byn[6]);
					totalB += (58-byn[7]);
					totalB += (51-byn[8]);
					totalB += (46-byn[9]);
					byn[hotbinB] -= 1;
				} else {hotbinB = 10;}
				//produce total number- smaller is closer to Benford real
				
				if (totalA < totalB)
				{
					byn[hotbinA] += 1;
					inputSample = floor(inputSample);
				}
				else
				{
					byn[hotbinB] += 1;
					inputSample = ceil(inputSample);
				}
				//assign the relevant one to the delay line
				//and floor/ceil signal accordingly
				
				totalA = byn[1] + byn[2] + byn[3] + byn[4] + byn[5] + byn[6] + byn[7] + byn[8] + byn[9];
				totalA /= 1000;
				if (totalA = 0) totalA = 1;
				byn[1] /= totalA;
				byn[2] /= totalA;
				byn[3] /= totalA;
				byn[4] /= totalA;
				byn[5] /= totalA;
				byn[6] /= totalA;
				byn[7] /= totalA;
				byn[8] /= totalA;
				byn[9] /= totalA;
				byn[10] /= 2; //catchall for garbage data
				break;
				
			case 12: 
			//slew only
			outputSample = (inputSample - lastSample)*trim;
			lastSample = inputSample;
			if (outputSample > 1.0) outputSample = 1.0;
			if (outputSample < -1.0) outputSample = -1.0;
			inputSample = outputSample;
		break;
				
		case 13: 
			//subs only
			gain = gaintarget;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			iirSampleA = (iirSampleA * altAmount) + (inputSample * iirAmount); inputSample = iirSampleA;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleB = (iirSampleB * altAmount) + (inputSample * iirAmount); inputSample = iirSampleB;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleC = (iirSampleC * altAmount) + (inputSample * iirAmount); inputSample = iirSampleC;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleD = (iirSampleD * altAmount) + (inputSample * iirAmount); inputSample = iirSampleD;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleE = (iirSampleE * altAmount) + (inputSample * iirAmount); inputSample = iirSampleE;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleF = (iirSampleF * altAmount) + (inputSample * iirAmount); inputSample = iirSampleF;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleG = (iirSampleG * altAmount) + (inputSample * iirAmount); inputSample = iirSampleG;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleH = (iirSampleH * altAmount) + (inputSample * iirAmount); inputSample = iirSampleH;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleI = (iirSampleI * altAmount) + (inputSample * iirAmount); inputSample = iirSampleI;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleJ = (iirSampleJ * altAmount) + (inputSample * iirAmount); inputSample = iirSampleJ;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleK = (iirSampleK * altAmount) + (inputSample * iirAmount); inputSample = iirSampleK;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleL = (iirSampleL * altAmount) + (inputSample * iirAmount); inputSample = iirSampleL;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleM = (iirSampleM * altAmount) + (inputSample * iirAmount); inputSample = iirSampleM;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleN = (iirSampleN * altAmount) + (inputSample * iirAmount); inputSample = iirSampleN;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleO = (iirSampleO * altAmount) + (inputSample * iirAmount); inputSample = iirSampleO;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleP = (iirSampleP * altAmount) + (inputSample * iirAmount); inputSample = iirSampleP;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleQ = (iirSampleQ * altAmount) + (inputSample * iirAmount); inputSample = iirSampleQ;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleR = (iirSampleR * altAmount) + (inputSample * iirAmount); inputSample = iirSampleR;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleS = (iirSampleS * altAmount) + (inputSample * iirAmount); inputSample = iirSampleS;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleT = (iirSampleT * altAmount) + (inputSample * iirAmount); inputSample = iirSampleT;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleU = (iirSampleU * altAmount) + (inputSample * iirAmount); inputSample = iirSampleU;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleV = (iirSampleV * altAmount) + (inputSample * iirAmount); inputSample = iirSampleV;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleW = (iirSampleW * altAmount) + (inputSample * iirAmount); inputSample = iirSampleW;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleX = (iirSampleX * altAmount) + (inputSample * iirAmount); inputSample = iirSampleX;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleY = (iirSampleY * altAmount) + (inputSample * iirAmount); inputSample = iirSampleY;
			inputSample *= gain; gain = ((gain-1)*0.75)+1;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;
			iirSampleZ = (iirSampleZ * altAmount) + (inputSample * iirAmount); inputSample = iirSampleZ;
			if (inputSample > 1.0) inputSample = 1.0;
			if (inputSample < -1.0) inputSample = -1.0;				
		break;
				
		case 14: 
			//silhouette
			bridgerectifier = fabs(inputSample)*1.57079633;
			if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
			bridgerectifier = 1.0-cos(bridgerectifier);
			if (inputSample > 0.0) inputSample = bridgerectifier;
			else inputSample = -bridgerectifier;
			
			silhouette = rand()/(double)RAND_MAX;
			silhouette -= 0.5;
			silhouette *= 2.0;
			silhouette *= fabs(inputSample);
			
			smoother = rand()/(double)RAND_MAX;
			smoother -= 0.5;
			smoother *= 2.0;
			smoother *= fabs(lastSample);
			lastSample = inputSample;
			
			silhouette += smoother;
			
			bridgerectifier = fabs(silhouette)*1.57079633;
			if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
			bridgerectifier = sin(bridgerectifier);
			if (silhouette > 0.0) silhouette = bridgerectifier;
			else silhouette = -bridgerectifier;
			
			inputSample = (silhouette + outSample) / 2.0;
			outSample = silhouette;
		break;
		}
		
		flip = !flip;
		//several dithers use this
		if (highRes) inputSample /= 256.0; //256 for 16/24 version
		if (dithering) inputSample /= 32768.0;
		
		if (dtype == 8) noiseShaping += inputSample - drySample;

		*destP = inputSample;
		destP += inNumChannels;
	}
}