StarterAudioUnitExample/TremoloUnit.cpp
/* |
<samplecode> |
<abstract>Tremolo Effect AU</abstract> |
</samplecode> |
*/ |
// 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 * M_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 * M_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); |
// 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 © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-19