FX Book Errata

The following are the known typos in the FX book as of 2/26/13 — all but one of these are in C++ code. Note: the code in the downloadable projects is correct!

Page 107:

The table of sin and cos values has a typo for 3pi/4 (the signs should be swapped)

— Incorrect:
cos(w) = 0.707 and sin(w) = -0.707

— Corrected:
cos(w) = -0.707 and sin(w) = +0.707
Page 186:

The “C” term in the coefficient calculations for the Butterworth BPF and BSF both have a typo in the tan() function; remove the fc term from the calculation. Thanks to Ernst Oosterveld for finding this!

— Incorrect:
tan(pifcBW/fs)

— Corrected:
tan(piBW/fs)
Page 188:

In the 2nd order APF calculations, the ThetaC term (found in the Beta calculation) is ThetaC = (2pifc)/fs

In addition the coefficient a2 is incorrectly printed as alpha2.
Page 202:

The “ac” factor in the first line of eq 6.46 on page 202 can be ignored – it doesn’t exist in the calculations. Thanks to Tom Beer for finding this. See the Bonus Projects on the Projects Page for the complete code for this LPF with corrections.
Page 203:

There is an error in the calculation of the g1, gz and gp coefficients for the Massberg LPF, second order only. There is also an error in the check for Q, it should reference the square root of 0.5, not 5.0. See the Bonus Projects on the Projects Page for the complete code for this LPF with corrections. Thanks to astute readers Christian Floisand and Ernst Oosterveld for finding and reporting this! The fully corrected equations are here (click on the equation to open in a new tab for zooming):

MassbergCorrection

Page 307:

The Wrapping statement should check m_fReadIndex >= 1024

— Incorrect:
// check for wrap
if(m_fReadIndex > 1024)
m_fReadIndex = m_fReadIndex – 1024;

— Corrected:
// check for wrap
if(m_fReadIndex >= 1024)
m_fReadIndex = m_fReadIndex – 1024;
Page 315:

The for loop limit for the triangle wave is incorrectly written as

for(int g=0; g=3; g++)

it should be

for(int g=0; g<=3; g++)

It will cause an infinite loop in the constructor:

— Corrected:

// triangle: += (-1)^g(1/(2g+1+^2)sin(w(2n+1)T)
// NOTE: the limit is 3 here because of the way the sum is constructed
// (look at the (2n+1) components
for(int g=0; g<=3; g++)
{
double n = double(g);
m_TriangleArray_BL5[i] += pow((fl oat)-1.0, (fl oat)n)*(1.0/pow((fl oat)(2*n + 1),              (float)2.0))*sin(2.0*pi*(2.0*n + 1)*i/1024.0);
}
Page 323:

The Wrapping statements should check m_fReadIndex >= 1024

— Incorrect:
// check for wrap
if(m_fReadIndex > 1024)
m_fReadIndex = m_fReadIndex – 1024;
if(m_fQuadPhaseReadIndex > 1024)
m_fQuadPhaseReadIndex = m_fQuadPhaseReadIndex – 1024;

— Corrected
// check for wrap
if(m_fReadIndex >= 1024)
m_fReadIndex = m_fReadIndex – 1024;
if(m_fQuadPhaseReadIndex >= 1024)
m_fQuadPhaseReadIndex = m_fQuadPhaseReadIndex – 1024;
Page 337:

A last minute change before publication prompted the removal of two of the LFO types; the result is that the ModDelayModule’s LFO waveform is improperly mapped. To correct this, change the second line in updateLFO() as follows:

— Incorrect:
m_LFO.m_uOscType = m_uLFOType; // this maps tri to sine and sin to ramp

— Corrected:
m_LFO.m_uOscType = m_uLFOType == 0 ? 2 : 0; // tri = 0 -> Oscillator’s tri = 2 and sin = 1 -> Oscillator’s sin = 0, see WTOscillator.h enum.
Page 344:

The same issue as above applies for the StereoQuadFlanger; to fix alter UpdateModDelays()

— Incorrect:
m_ModDelayLeft.m_uLFOType = m_uLFOType; // this maps tri to sine and sin to ramp
m_ModDelayRight.m_uLFOType = m_uLFOType; // this maps tri to sine and sin to ramp

— Corrected:
m_ModDelayLeft.m_uLFOType = m_uLFOType == 0 ? 2 : 0; // tri = 0 -> Oscillator’s tri = 2 and sin = 1 -> Oscillator’s sin = 0, see WTOscillator.h enum.
m_ModDelayRight.m_uLFOType = m_uLFOType == 0 ? 2 : 0; // tri = 0 -> Oscillator’s tri = 2 and sin = 1 -> Oscillator’s sin = 0, see WTOscillator.h enum.
Page 339:

The case statement for the chorus contains a line that needs to be removed:

m_DDL.m_fInputAttenuation = 1.0; is no longer part of this object; it was used in the TZF Challenge

— Incorrect:

case Chorus:
{
m_fMinDelay_mSec = 5;
m_fMaxDelay_mSec = 30;
m_DDL.m_fWetLevel_pct = 50.0;
m_DDL.m_fInputAttenuation = 1.0;
break;
}
— Correct

case Chorus:
{
m_fMinDelay_mSec = 5;
m_fMaxDelay_mSec = 30;
m_DDL.m_fWetLevel_pct = 50.0;
break;
}
Page 339:

The function Definition line is missing:

— Incorrect
// calculateDelayOffset():
/*
fLFOSample: a value from 0.0 to 1.0 from the LFO object
returns: the calculated delay time in mSec for each effect
NOTES: – the range for the fl anger/vibrato is simply mapped from min to max
starting at min: fLFOSample*(m_fMaxDelay_mSec – m_fMinDelay_mSec)) +
m_fMinDelay_mSec
– the range for the Chorus includes the starting offset fStart = m_fMinDelay_mSec + m_fChorusOffset;
*/

if(m_uModType == Flanger || m_uModType == Vibrato)
{
// flanger 0->1 gets mapped to 0->maxdelay
etc…
— Correct
// calculateDelayOffset():
/*
fLFOSample: a value from 0.0 to 1.0 from the LFO object
returns: the calculated delay time in mSec for each effect
NOTES: – the range for the fl anger/vibrato is simply mapped from min to max
starting at min: fLFOSample*(m_fMaxDelay_mSec – m_fMinDelay_mSec)) +
m_fMinDelay_mSec
– the range for the Chorus includes the starting offset fStart = m_fMinDelay_mSec + m_fChorusOffset;
*/
float CModDelayModule::calculateDelayOffset(float fLFOSample)
{
if(m_uModType == Flanger || m_uModType == Vibrato)
{
// flanger 0->1 gets mapped to 0->maxdelay
etc…
}
Page 344:

The return statement is missing from the function:

— Incorrect:

bool __stdcall CStereoQuadFlanger::userInterfaceChange(int nControlIndex)
{
// just do a brute force update of all
updateModDelays();
}
— Corrected

bool __stdcall CStereoQuadFlanger::userInterfaceChange(int nControlIndex)
{
// just do a brute force update of all
updateModDelays();

return true;
}
Page 402:

The default m_f_RT60 should be 1800 mSec, not 1000 mSec as shown in Table 11.9
Page 439:

The APF calculation code is for a 2nd order APF whereas the Phaser uses the 1st order APF on page 188 (section 6.6.7). Furthermore, in this 2nd order APF code (p.439) the b2 coefficient should be -alpha, not -1.0. The original analog phaser from National Semiconductor (on which this project is based) uses 1st order APF sections.
Page 479:

The function prototypes are incorrect; they should be identical to the normal DynamicsProcessor prototypes

— Incorrect
// calcualte the compressor G(n) value from the Detector output
float calcCompressorGain(fl oat fDetectorValue, float fThreshold, float fRatio);

// calcualte the downward expander G(n) value from the Detector output
float calcDownwardExpanderGain( fl oat fDetectorValue, float fThreshold, float fRatio);
— Corrected:
// calculate the compressor G(n) value from the Detector output
float calcCompressorGain(float fDetectorValue, float fThreshold, float fRatio, float fKneeWidth, bool bLimit);

// calculate the downward expander G(n) value from the Detector output
float calcDownwardExpanderGain(float fDetectorValue, float fThreshold, float fRatio, float fKneeWidth, bool bGate);
Page 493:

The function calculateTremoloGainFactor should be calculateGainFactor

— Incorrect
if(m_uMode == Tremolo)
fGnL = calculateTremoloGainFactor(fYn);

— Corrected
if(m_uMode == Tremolo)
fGnL = calculateGainFactor(fYn);