CoreAudio/PublicUtility/CAAUProcessor.h
/* |
File: CAAUProcessor.h |
Abstract: Part of CoreAudio Utility Classes |
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) 2014 Apple Inc. All Rights Reserved. |
*/ |
#ifndef __CAAUProcessor_h__ |
#define __CAAUProcessor_h__ |
#include <AudioToolbox/AudioToolbox.h> |
#include "CAStreamBasicDescription.h" |
#include "CAAudioUnit.h" |
#include "AUOutputBL.h" |
#if TARGET_OS_IPHONE |
#include <AssertMacros.h> |
#endif |
/* |
This class wraps an AU (using the CAAudioUnit helper class) to use that AU for processing data |
It can be used in a RealTime context (rendering data with RT constraints) or in an OffLine context |
such as using an AU to process data that is stored in a file, and on which there are no |
RT constraints to be imposed. |
Order of operations: |
Create an instance |
Establish an Input Callback |
Initialize the AU to the audio formats and whether it is going to process in an offline or RT context |
Preflight |
while (...) |
Render // based on your calling context |
PostProcess if needed (only in OL case) |
After any initialization, preflighting is required. |
Parameter Values on the AU should be set just before each call to Render. The sampleFrameOffsets |
supplied when setting those values are an offset into that next render buffer's numFrames. |
RT vs OT is determined by whether the inputSampleCount is set during Initialization, thus |
this class can move the AU between RT and OL contexts. If you are using an AU of type 'auol' |
(Offline), then it cannot be used in a RT context. |
The CAAUProcessor will only call your Input Callback for input when it needs valid input. |
This input callback will contain a sample time that indicates where within your input |
you should read data from, where after preflighting, the first output data produces is for |
the output sample count of zero. |
MaxFrames should be set before initialisation (or is also passed in to the Reinitialize API) |
If RT, then PostProcessing will *never* be required, nor will Render ever return an isDone value |
If OL, then Render will at some point return isDone==true, and then also indicate if PostProcessing is required |
If using a Format Converter AU in offline context, then the only way for it to determine that you've finished |
is for the Input callback to return an error. This means ultimately, that you'll have potentially longer output |
than you should have. The only way to manage this is for the caller to know the relationship between |
input to output samples that will be required, and to call Render for just that amount of output samples |
then return an error, so the tail and latency can be processed. |
Tail and Latency are *only* calculated at initialisation.. Some AU's may change these properties based on |
either different presets or parameter settings... Ideally, this class should listen to those properties |
and recalculate its values based on those changes. |
*/ |
class CAAUProcessor { |
public: |
// by default this is set to process offline |
CAAUProcessor (const CAComponent& inComp); |
~CAAUProcessor (); |
CAAudioUnit& AU() { return mUnit; } |
const CAAudioUnit& AU() const { return mUnit; } |
#pragma mark __Setup APIs |
bool IsOfflineContext () const { return mNumInputSamples != 0; } |
// this contains the callback and client data that the AU |
// uses to call when it needs input |
OSStatus EstablishInputCallback (AURenderCallbackStruct &inInputCallback); |
OSStatus SetAUPreset (CFPropertyListRef inPreset); |
OSStatus SetAUPresetIndex (SInt32 inPresetIndex); |
OSStatus SetParameter (AudioUnitParameterID inID, AudioUnitScope scope, AudioUnitElement element, |
Float32 value, UInt32 bufferOffsetFrames = 0); |
// a result of zero here signifies an error |
UInt32 MaxFramesPerRender () const; |
// this call can return an error if the AU is initialized - see Reinitialize |
OSStatus SetMaxFramesPerRender (UInt32 inMaxFrames); |
// Prepares the AU for processing at the specified format |
// only doing n-n channel processing here.. could extend this for n-m channel processing easily enough |
// if you are processing using an Offline AU, you HAVE to specify the numInputSamples to process |
// if you are processing real-time, then inNumInputSamples should be zero |
OSStatus Initialize (const CAStreamBasicDescription &inIODesc, UInt64 inNumInputSamples = 0) |
{ |
return Initialize (inIODesc, inIODesc, inNumInputSamples); |
} |
OSStatus Initialize (const CAStreamBasicDescription &inInputDesc, |
const CAStreamBasicDescription &inOutputDesc, |
UInt64 inNumInputSamples = 0); |
// takes the existing format state of the AU, but resets the AUProcessor so that: |
// applies an optional new number of max frames |
// the AUProcessor will have to be Preflighted again after this call |
// Use this when you want to keep the same formats, same input sample count, |
// but want to re-initialize the AU to use a new num max frames and prepare it |
// appropriately... |
OSStatus Reinitialize (UInt32 inNewMaxFrames); |
#pragma mark __Render APIs |
// Preflighting - you HAVE to preflight |
// returns noErr when preflighting is done |
// Offline Context: |
// Offline AU's require preflighting, even if they will end up doing no work. |
// In the case where the AU is offline the latency samples are trimmed and not returned |
// from the consequent Render calls |
// If you are NOT an offline AU then the first time Render is called, |
// there will need to be some consumption of the input before you will get any valid output data. |
// This is determined by the value of the AU's reported latency. So, in this case preflight |
// will consume those initial samples and then return. |
// Your InputCallback MUST provide the initial data that is to be processed. |
// RealTime Context: |
// (1 No priming. In this case inProcessPreceedingTail is false (the default) and no |
// processing is done on the AU. However, because preflight is the first time that Rendering |
// is called on an AU we can presume that this is outside the context of the processing that |
// is occuring in the host application. To avoid taking page faults, priming cold memory, etc. |
// when the AU *is* consequently used to render real data, the AU is prerolled and reset if this is the case. |
// (2) Prime the AU's buffers with input data that preceeds the portion of audio you |
// want to process. This amount of input data is equivalent to the tail time of the AU |
// which expresses how long it takes for an sample on input to disappear from the output |
// (Think of a delay or a reverb) |
// So, by setting inProcessPreceedingTail to true, the preflight call will consume |
// the tail time samples of input (which should be valid input BEFORE the data to be processed) |
// So, your input proc here will HAVE to read samples from the preceeding input. The time |
// stamps to your input proc in this case can be interpreted as 0 is the start of the preceeding data |
// and you'll need to read up to the numSamples of tail time of the preceeding data. |
// It discards the output results of the preflighting. If you don't have any previous data |
// with which to prepare the AU, then you do NOT need to take this path and can do the step below. |
// In both of these cases the inital latency frames are *not* trimmed from |
// the output that will be generated by a consequent call to render. |
OSStatus Preflight (bool inProcessPreceedingTail = false); |
// this can be used with an OfflineAU to do a gradual preflight (so you could for instance, |
// present a progress indicator. The Preflight call will do it all at once. |
// If you break out of the preflight before you are done, then you HAVE to reinitialise the AU |
// before you can either do another preflight or do a render, otherwise the Render call will produce |
// invalid results. |
OSStatus OfflineAUPreflight (UInt32 inNumFrames, bool &outIsDone); |
// Here's the major stage that produces output. |
// You pass in two bool flags and an ioNumFrames.... |
// So ioNumFrames will be *untouched* if outOLCompleted == false |
// if outOLCompleted is true, ioNumFrames contains the number of valid frames within the ioData data that are valid data |
// (ioData's pointers and sizes will also have been resized to match these number of frames) |
// outOLRequiresPostProcess should also be checked when outOLCompleted is true, |
// to see if any post-processing is required |
// if rendering in a RT context, then neither outOLCompleted or outOLRequiresPostProcess is required |
// if rendering in an OL context, both must be specified. |
// Caller MUST provide an ABL that is set up in the previously specified output format |
// If you receive kAudioUnitErr_InvalidOfflineRender when dealing with an OfflineAU Type, then you have either not |
// preflighted it (and the AU requires it) or you have changed some state (its num input samples, its start offest) |
// and it needs to be re-preflighted. In that case, preflight the AU again first. |
OSStatus Render (AudioBufferList *ioData, |
UInt32 &ioNumFrames, |
bool &outIsSilence, |
bool *outOLCompleted = NULL, |
bool *outOLRequiresPostProcess = NULL); |
// call this method if outOLRequiresPostProcess returned true upon rendering completion |
OSStatus PostProcess (AudioBufferList *ioData, UInt32 &ioNumFrames, bool &outIsSilence, bool &outDone); |
// This method returns how many input samples will need to be provided before |
// valid output data is produced. |
UInt32 LatencySampleCount () const { return mLatencySamples; } |
UInt32 TailSampleCount () const { return mTailSamples; } |
UInt64 InputSampleCount () const { return mNumInputSamples; } |
// the class maintains the TimeStamp from render call to render call |
// this value is the sample count that will be used in the *next* render call |
Float64 SampleTime () const { return mRenderTimeStamp.mSampleTime; } |
// in some settings (for instance a delay with 100% feedback) tail time is essentially infinite |
// so you should safeguard the final OL render stage (post process) which is aimed at pulling the tail through |
// default is 10 seconds |
Float64 GetMaxTailTime () const { return mMaxTailTime; } |
// this ONLY takes affect when you initialise the processor, it doesn't change that AU's tail time, |
// but just the max amount of time that this object will allow the AU to post-process data |
// set this to 0, to use the AU's tail time with no adjustment |
void SetMaxTailTime (Float64 inTailTimeSecs) { mMaxTailTime = inTailTimeSecs; } |
#if !TARGET_OS_IPHONE |
// if this is NULL, then there is no explicit (optional or required) preflight requirement of the AU |
// if this is valid, then the AU either requires or optionally requires preflighting. The default |
// behaviour in this case is that the processor will preflight. This name can be used to preset |
// to the user. If the AU doesn't present a name, but preflighting is optional/required, then the |
// processor return "Preflight" as its string. |
// the String should be released by the caller. |
CFStringRef GetOLPreflightName () const; |
// Returns true if this is processor is an offline AU, and that AU (whether optional or required) |
// needs preflighting |
bool OfflineAUNeedsPreflight () const; |
// this provides a rough approximation of the progress of an Offline Processing operation (both for the |
// preflight and the render phases) |
Float32 GetOLPercentComplete (); |
#endif |
private: |
CAAudioUnit mUnit; |
UInt32 mLatencySamples; |
UInt32 mTailSamples; |
UInt32 mTailSamplesToProcess; |
UInt64 mNumInputSamples; |
AudioTimeStamp mRenderTimeStamp; |
bool mPreflightDone; |
AUOutputBL * mPreflightABL; |
SInt32 mTailSamplesRemaining; |
AURenderCallbackStruct mUserCallback; |
Float64 mMaxTailTime; |
Float32 mLastPercentReported; |
bool IsOfflineAU () const { return mUnit.Comp().Desc().IsOffline(); } |
void CalculateRemainderSamples (Float64 inSampleRate); |
OSStatus DoInitialisation (const CAStreamBasicDescription &inInputFormat, |
const CAStreamBasicDescription &inOutputFormat, |
UInt64 inNumInputSamples, |
UInt32 inMaxFrames); |
// stop automatic copying of this object |
CAAUProcessor () {} |
CAAUProcessor (const CAAUProcessor &c) {} |
CAAUProcessor& operator= (const CAAUProcessor& c) { return *this; } |
}; |
#endif //__CAAUProcessor_h__ |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-07-08