Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Relevant replacement documents include:
TremoloUnit.cpp
/* |
File: TremoloUnit.cpp |
Abstract: TremoloUnit.h |
Version: 1.1 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
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 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. |
Copyright (C) 2012 Apple Inc. All Rights Reserved. |
*/ |
// This file defines the TremoloUnit class, as well as the TremoloUnitEffectKernel |
// helper class. |
#include "TremoloUnit.h" |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
AUDIOCOMPONENT_ENTRY(AUBaseFactory, TremoloUnit) |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// The COMPONENT_ENTRY macro is required for the Mac OS X Component Manager to recognize and |
// use the audio unit |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::TremoloUnit |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// The constructor for new TremoloUnit audio units |
TremoloUnit::TremoloUnit (AudioUnit component) : AUEffectBase (component) { |
// This method, defined in the AUBase superclass, ensures that the required audio unit |
// elements are created and initialized. |
CreateElements (); |
// Invokes the use of an STL vector for parameter access. |
// See AUBase/AUScopeElement.cpp |
Globals () -> UseIndexedParameters (kNumberOfParameters); |
// During instantiation, sets up the parameters according to their defaults. |
// The parameter defaults should correspond to the settings for the default preset. |
SetParameter ( |
kParameter_Frequency, |
kDefaultValue_Tremolo_Freq |
); |
SetParameter ( |
kParameter_Depth, |
kDefaultValue_Tremolo_Depth |
); |
SetParameter ( |
kParameter_Waveform, |
kDefaultValue_Tremolo_Waveform |
); |
// Also during instantiation, sets the preset menu to indicate the default preset, |
// which corresponds to the default parameters. It's possible to set this so a |
// fresh audio unit indicates the wrong preset, so be careful to get it right. |
SetAFactoryPresetAsCurrent ( |
kPresets [kPreset_Default] |
); |
#if AU_DEBUG_DISPATCHER |
mDebugDispatcher = new AUDebugDispatcher (this); |
#endif |
} |
#pragma mark ____Parameters |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::GetParameterInfo |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Called by the audio unit's view; provides information needed for the view to display the |
// audio unit's parameters |
ComponentResult TremoloUnit::GetParameterInfo ( |
AudioUnitScope inScope, |
AudioUnitParameterID inParameterID, |
AudioUnitParameterInfo &outParameterInfo |
) { |
ComponentResult result = noErr; |
// Adds two flags to all parameters for the audio unit, indicating to the host application |
// that it should consider all the audio unitÕs parameters to be readable and writable. |
outParameterInfo.flags = |
kAudioUnitParameterFlag_IsWritable | kAudioUnitParameterFlag_IsReadable; |
// All three parameters for this audio unit are in the "global" scope. |
if (inScope == kAudioUnitScope_Global) { |
switch (inParameterID) { |
case kParameter_Frequency: |
// Invoked when the view needs information for the kTremoloParam_Frequency |
// parameter; defines how to represent this parameter in the user interface. |
AUBase::FillInParameterName ( |
outParameterInfo, |
kParamName_Tremolo_Freq, |
false |
); |
outParameterInfo.unit = kAudioUnitParameterUnit_Hertz; |
// Sets the unit of measurement for the Frequency parameter to Hertz. |
outParameterInfo.minValue = kMinimumValue_Tremolo_Freq; |
// Sets the minimum value for the Frequency parameter. |
outParameterInfo.maxValue = kMaximumValue_Tremolo_Freq; |
// Sets the maximum value for the Frequency parameter. |
outParameterInfo.defaultValue = kDefaultValue_Tremolo_Freq; |
// Sets the default value for the Frequency parameter. |
outParameterInfo.flags |= kAudioUnitParameterFlag_DisplayLogarithmic; |
// Adds a flag to indicate to the host that it should use a logarithmic |
// control for the Frequency parameter. |
break; |
case kParameter_Depth: |
// Invoked when the view needs information for the kTremoloParam_Depth parameter. |
AUBase::FillInParameterName ( |
outParameterInfo, |
kParamName_Tremolo_Depth, |
false |
); |
outParameterInfo.unit = kAudioUnitParameterUnit_Percent; |
outParameterInfo.minValue = kMinimumValue_Tremolo_Depth; |
outParameterInfo.maxValue = kMaximumValue_Tremolo_Depth; |
outParameterInfo.defaultValue = kDefaultValue_Tremolo_Depth; |
break; |
case kParameter_Waveform: |
// Invoked when the view needs information for the kTremoloParam_Waveform parameter. |
AUBase::FillInParameterName ( |
outParameterInfo, |
kParamName_Tremolo_Waveform, |
false |
); |
outParameterInfo.unit = kAudioUnitParameterUnit_Indexed; |
// Sets the unit of measurement for the Waveform parameter to "indexed," allowing |
// it to be displayed as a pop-up menu in the generic view. The following three |
// statements set the minimum, maximum, and default values for the depth parameter. |
// All three are required for proper functioning of the parameter's user interface. |
outParameterInfo.minValue = kSineWave_Tremolo_Waveform; |
outParameterInfo.maxValue = kSquareWave_Tremolo_Waveform; |
outParameterInfo.defaultValue = kDefaultValue_Tremolo_Waveform; |
break; |
default: |
result = kAudioUnitErr_InvalidParameter; |
break; |
} |
} else { |
result = kAudioUnitErr_InvalidParameter; |
} |
return result; |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::GetParameterValueStrings |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Provides the strings for the Waveform popup menu in the generic view |
ComponentResult TremoloUnit::GetParameterValueStrings ( |
AudioUnitScope inScope, |
AudioUnitParameterID inParameterID, |
CFArrayRef *outStrings |
) { |
if ((inScope == kAudioUnitScope_Global) && (inParameterID == kParameter_Waveform)) { |
// This method applies only to the waveform parameter, which is in the global scope. |
// When this method gets called by the AUBase::DispatchGetPropertyInfo method, which |
// provides a null value for the outStrings parameter, just return without error. |
if (outStrings == NULL) return noErr; |
// Defines an array that contains the pop-up menu item names. |
CFStringRef strings [] = { |
kMenuItem_Tremolo_Sine, |
kMenuItem_Tremolo_Square |
}; |
// Creates a new immutable array containing the menu item names, and places the array |
// in the outStrings output parameter. |
*outStrings = CFArrayCreate ( |
NULL, |
(const void **) strings, |
(sizeof (strings) / sizeof (strings [0])), |
NULL |
); |
return noErr; |
} |
return kAudioUnitErr_InvalidParameter; |
} |
#pragma mark ____Properties |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::GetPropertyInfo |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
ComponentResult TremoloUnit::GetPropertyInfo ( |
// This audio unit doesn't define any custom properties, so it uses this generic code for |
// this method. |
AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
UInt32 &outDataSize, |
Boolean &outWritable |
) { |
return AUEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable); |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::GetProperty |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
ComponentResult TremoloUnit::GetProperty ( |
// This audio unit doesn't define any custom properties, so it uses this generic code for |
// this method. |
AudioUnitPropertyID inID, |
AudioUnitScope inScope, |
AudioUnitElement inElement, |
void *outData |
) { |
return AUEffectBase::GetProperty (inID, inScope, inElement, outData); |
} |
#pragma mark ____Factory Presets |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::GetPresets |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// For users to be able to use the factory presets you define, you must add a generic |
// implementation of the GetPresets method. The code here works for any audio unit that can |
// support factory presets. |
// The GetPresets method accepts a single parameter, a pointer to a CFArrayRef object. This |
// object holds the factory presets array generated by this method. The array contains just |
// factory preset numbers and names. The host application uses this array to set up its |
// factory presets menu and when calling the NewFactoryPresetSet method. |
ComponentResult TremoloUnit::GetPresets ( |
CFArrayRef *outData |
) const { |
// Checks whether factory presets are implemented for this audio unit. |
if (outData == NULL) return noErr; |
// Instantiates a mutable Core Foundation array to hold the factory presets. |
CFMutableArrayRef presetsArray = CFArrayCreateMutable ( |
NULL, |
kNumberPresets, |
NULL |
); |
// Fills the factory presets array with values from the definitions in the TremoloUnit.h |
// file. |
for (int i = 0; i < kNumberPresets; ++i) { |
CFArrayAppendValue ( |
presetsArray, |
&kPresets [i] |
); |
} |
// Stores the factory presets array at the outData location. |
*outData = (CFArrayRef) presetsArray; |
return noErr; |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::NewFactoryPresetSet |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// The NewFactoryPresetSet method defines all the factory presets for an audio unit. Basically, |
// for each preset, it invokes a series of SetParameter calls. |
// This method takes a single argument of type AUPreset, a structure containing a factory |
// preset name and number. |
OSStatus TremoloUnit::NewFactoryPresetSet ( |
const AUPreset &inNewFactoryPreset |
) { |
// Gets the number of the desired factory preset. |
SInt32 chosenPreset = inNewFactoryPreset.presetNumber; |
if ( |
// Tests whether the desired factory preset is defined. |
chosenPreset == kPreset_Slow || |
chosenPreset == kPreset_Fast |
) { |
// This 'for' loop, and the 'if' statement that follows it, allow for noncontiguous preset |
// numbers. |
for (int i = 0; i < kNumberPresets; ++i) { |
if (chosenPreset == kPresets[i].presetNumber) { |
//Selects the appropriate case statement based on the factory preset number. |
switch (chosenPreset) { |
// The settings for the "Slow & Gentle" factory preset. |
case kPreset_Slow: |
SetParameter ( |
kParameter_Frequency, |
kParameter_Preset_Frequency_Slow |
); |
SetParameter ( |
kParameter_Depth, |
kParameter_Preset_Depth_Slow |
); |
SetParameter ( |
kParameter_Waveform, |
kParameter_Preset_Waveform_Slow |
); |
break; |
// The settings for the "Fast & Hard" factory preset. |
case kPreset_Fast: |
SetParameter ( |
kParameter_Frequency, |
kParameter_Preset_Frequency_Fast |
); |
SetParameter ( |
kParameter_Depth, |
kParameter_Preset_Depth_Fast |
); |
SetParameter ( |
kParameter_Waveform, |
kParameter_Preset_Waveform_Fast |
); |
break; |
} |
// Updates the preset menu in the generic view to display the new factory preset. |
SetAFactoryPresetAsCurrent ( |
kPresets [i] |
); |
return noErr; |
} |
} |
} |
return kAudioUnitErr_InvalidProperty; |
} |
#pragma mark ____TremoloUnitEffectKernel |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::TremoloUnitKernel::TremoloUnitKernel() |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// This is the constructor for the TremoloUnitKernel helper class, which holds the DSP code |
// for the audio unit. TremoloUnit is an n-to-n audio unit; one kernel object gets built for |
// each channel in the audio unit. |
// |
// The first line of the method consists of the constructor method declarator and constructor- |
// initializer. In addition to calling the appropriate superclasses, this code initializes two |
// member variables: |
// |
// mCurrentScale: a factor for correlating points in the current wave table to |
// the audio signal sampling frequency. to produce the desired |
// tremolo frequency |
// mSamplesProcessed: a global count of samples processed. it allows the tremolo effect |
// to be continuous over data input buffer boundaries |
// |
// (In the Xcode template, the header file contains the call to the superclass constructor.) |
TremoloUnit::TremoloUnitKernel::TremoloUnitKernel (AUEffectBase *inAudioUnit ) : AUKernelBase (inAudioUnit), |
mSamplesProcessed (0), mCurrentScale (0) |
{ |
// Generates a wave table that represents one cycle of a sine wave, normalized so that |
// it never goes negative and so it ranges between 0 and 1; this sine wave specifies |
// how to vary the volume during one cycle of tremolo. |
for (int i = 0; i < kWaveArraySize; ++i) { |
double radians = i * 2.0 * pi / kWaveArraySize; |
mSine [i] = (sin (radians) + 1.0) * 0.5; |
} |
// Does the same for a pseudo square wave, with nice rounded corners to avoid pops. |
for (int i = 0; i < kWaveArraySize; ++i) { |
double radians = i * 2.0 * pi / kWaveArraySize; |
radians = radians + 0.32; // shift the wave over for a smoother start |
mSquare [i] = |
( |
sin (radians) + // Sums the odd harmonics, scaled for a nice final waveform |
0.3 * sin (3 * radians) + |
0.15 * sin (5 * radians) + |
0.075 * sin (7 * radians) + |
0.0375 * sin (9 * radians) + |
0.01875 * sin (11 * radians) + |
0.009375 * sin (13 * radians) + |
0.8 // Shifts the value so it doesn't go negative. |
) * 0.63; // Scales the waveform so the peak value is close |
// to unity gain. |
} |
// Gets the samples per second of the audio stream provided to the audio unit. |
// Obtaining this value here in the constructor assumes that the sample rate |
// will not change during one instantiation of the audio unit. |
mSampleFrequency = GetSampleRate (); |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::TremoloUnitKernel::Reset() |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// Because we're calculating each output sample based on a unique input sample, there's no |
// need to clear any buffers. We simply reinitialize the variables that were initialized on |
// instantiation of the kernel object. |
void TremoloUnit::TremoloUnitKernel::Reset() { |
mCurrentScale = 0; |
mSamplesProcessed = 0; |
} |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// TremoloUnit::TremoloUnitKernel::Process |
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
// This method contains the DSP code. |
void TremoloUnit::TremoloUnitKernel::Process ( |
const Float32 *inSourceP, // The audio sample input buffer. |
Float32 *inDestP, // The audio sample output buffer. |
UInt32 inSamplesToProcess, // The number of samples in the input buffer. |
UInt32 inNumChannels, // The number of input channels. This is always equal to 1 |
// because there is always one kernel object instantiated |
// per channel of audio. |
bool &ioSilence // A Boolean flag indicating whether the input to the audio |
// unit consists of silence, with a TRUE value indicating |
// silence. |
) { |
// Ignores the request to perform the Process method if the input to the audio unit is silence. |
if (!ioSilence) { |
// Assigns a pointer variable to the start of the audio sample input buffer. |
const Float32 *sourceP = inSourceP; |
// Assigns a pointer variable to the start of the audio sample output buffer. |
Float32 *destP = inDestP, |
inputSample, // The current audio sample to process. |
outputSample, // The current audio output sample resulting from one iteration of the |
// processing loop. |
tremoloFrequency, // The tremolo frequency requested by the user via the audio unit's view. |
tremoloDepth, // The tremolo depth requested by the user via the audio unit's view. |
samplesPerTremoloCycle, // The number of audio samples in one cycle of the tremolo waveform. |
rawTremoloGain, // The tremolo gain for the current audio sample, as stored in the wave table. |
tremoloGain; // The adjusted tremolo gain for the current audio sample, considering the |
// Depth parameter. |
int tremoloWaveform; // The tremolo waveform type requested by the user via the audio unit's view. |
// Once per input buffer, gets the tremolo frequency (in Hz) from the user |
// via the audio unit view. |
tremoloFrequency = GetParameter (kParameter_Frequency); |
// Once per input buffer, gets the depth (in percent) from the user via |
// the audio unit view. |
tremoloDepth = GetParameter (kParameter_Depth); |
// Once per input buffer, gets the tremolo waveform type from the user via |
// the audio unit view. |
tremoloWaveform = (int) GetParameter (kParameter_Waveform); |
if (tremoloWaveform != kSineWave_Tremolo_Waveform |
&& tremoloWaveform != kSquareWave_Tremolo_Waveform) |
tremoloWaveform = kSineWave_Tremolo_Waveform; |
// Assigns a pointer to the wave table for the user-selected tremolo wave form. |
if (tremoloWaveform == kSineWave_Tremolo_Waveform) { |
waveArrayPointer = &mSine [0]; |
} else { |
waveArrayPointer = &mSquare [0]; |
} |
// Performs bounds checking on the parameters. |
if (tremoloFrequency < kMinimumValue_Tremolo_Freq) |
tremoloFrequency = kMinimumValue_Tremolo_Freq; |
if (tremoloFrequency > kMaximumValue_Tremolo_Freq) |
tremoloFrequency = kMaximumValue_Tremolo_Freq; |
if (tremoloDepth < kMinimumValue_Tremolo_Depth) |
tremoloDepth = kMinimumValue_Tremolo_Depth; |
if (tremoloDepth > kMaximumValue_Tremolo_Depth) |
tremoloDepth = kMaximumValue_Tremolo_Depth; |
// Calculates the number of audio samples per cycle of tremolo frequency. |
samplesPerTremoloCycle = mSampleFrequency / tremoloFrequency; |
// Calculates the scaling factor to use for applying the wave table to the current sampling |
// frequency and tremolo frequency. |
mNextScale = kWaveArraySize / samplesPerTremoloCycle; |
/* |
An explanation of the scaling factor (mNextScale) |
------------------------------------------------- |
Say that the audio sample frequency is 10 kHz and that the tremolo frequency is |
10.0 Hz. the number of audio samples per tremolo cycle is then 1,000. |
For a wave table of length 1,000, the scaling factor is then unity (1.0). This means |
that the wave table happens to be the exact size needed for each point in the table |
to correspond to exactly one sample. |
If the tremolo frequency slows to 1.0 Hz, then the number of samples per tremolo |
cycle rises to 10,000. The scaling factor is then 0.1. This means that every 10th |
element of the wave table array corresponds to a sample. |
If the tremolo frequency increases to 20 Hz, the samples per tremolo cycle lowers to |
500. The scaling factor is then 1,000/500 = 2.0. In this case, two samples in a row |
need to make use of the same point in the wave table. |
*/ |
// The sample processing loop: processes the current batch of samples, one sample at a time. |
for (int i = inSamplesToProcess; i > 0; --i) { |
// The following statement calculates the position in the wave table ("index") to |
// use for the current sample. This position, along with the calculation of |
// mNextScale, is the only subtle math for this audio unit. |
// |
// "index" is the position marker in the wave table. The wave table is an array; |
// index varies from 0 to kWaveArraySize. |
// |
// "index" is also the number of samples processed since the last |
// counter reset, divided by the number of samples that play during one pass |
// through the wave table, modulo the size of the wave table (see "An explanation...", |
// above). |
int index = static_cast<long>(mSamplesProcessed * mCurrentScale) % kWaveArraySize; |
// If the user has moved the tremolo frequency slider, changes the scale factor |
// at the next positive zero crossing of the tremolo sine wave and resets the |
// mSamplesProcessed value so it stays in sync with the index position. |
if ((mNextScale != mCurrentScale) && (index == 0)) { |
mCurrentScale = mNextScale; |
mSamplesProcessed = 0; |
} |
// If the audio unit runs for a long time without the user moving the |
// tremolo frequency slider, resets the mSamplesProcessed value at the |
// next positive zero crossing of the tremolo sine wave. |
if ((mSamplesProcessed >= sampleLimit) && (index == 0)) |
mSamplesProcessed = 0; |
// Gets the raw tremolo gain from the appropriate wave table. |
rawTremoloGain = waveArrayPointer [index]; |
// Calculates the final tremolo gain according to the depth setting. |
tremoloGain = (rawTremoloGain * tremoloDepth - tremoloDepth + 100.0) * 0.01; |
// Gets the next input sample. |
inputSample = *sourceP; |
// Calculates the next output sample. |
outputSample = (inputSample * tremoloGain); |
// Stores the output sample in the output buffer. |
*destP = outputSample; |
// Advances to the next sample location in the input and output buffers. |
sourceP += 1; |
destP += 1; |
// Increments the global samples counter. |
mSamplesProcessed += 1; |
} |
} |
} |
Copyright © 2012 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2012-10-16