Hi Roman,

What you're trying to do cannot be supported in real time. Since you're not 
able to use offline effects, you'll have to change your approach. Instead of 
trying to pull from a timestamp that is ahead of the current time, you need to 
introduce latency in your plugin. That way, you can always pull from the 
current time and not violate the laws of causality.

Your AU should implement GetLatency to return a Float64 representing the number 
of seconds needed for 2*N frames. You can also implement GetTailTime if your 
effect has any sort of feedback that would linger after the input audio has 
stopped.

Once you implement GetLatency, AU Hosts like Logic will automatically send your 
plugin the audio data in advance of when it is needed, so that the output audio 
is time aligned with the other AU plugins in the graph, even though the input 
audio is not aligned. This works great for all graphs except those that involve 
live inputs, which necessarily must introduce latency.

Basically, with latency your AU will produce silence for the first N frames 
because your algorithm will not have enough data to do its processing. You'll 
have to save those input samples in a buffer that you maintain. But after those 
first few silent frames are output, your algorithm can use the 2*N frames of 
data that is needed to produce N frames of new output. Your buffer will need to 
be circular so that you can continually maintain 2*N frames even though you're 
only getting N new frames each Process call.

The above is fairly simple to implement in three stages. The first stage fills 
an input queue, maintaining a count of valid samples. The second stage does 
nothing until 2*N frames are available, and then utilizes 2*N frames of input 
to proceed N frames of output, but only consumes N frames of input (leaving the 
other N frames of input for the next Process). The third stage outputs silence 
if there is nothing in the output queue, but consumes the appropriate number of 
frames for the Process call if they're available.

One tricky aspect of the above is that an AU is supposed to support Process 
calls with fewer than the expected N frames of input / output. In those cases 
where your AU is asked for fewer than N frames, you'll have to add the incoming 
samples to the input queue and bump the count by M (the actual number of 
samples requested), but your second stage might not be able to do anything 
until there are N new samples available. It's actually fine for your middle 
stage to do nothing, so long as the queues are all updated properly on each 
call. The output queue will need to consume only M samples, but once the output 
queue has been primed during the initial silent latency phase it should always 
have M samples available.

In other words, you must disconnect the actual AU input and output from your 
algorithm such that the surrounding queues deal with the number of samples that 
the AU Host is controlling, while your inner stage always processes 2*N frames 
to consume N frames of input and add N frames to the output. You're basically 
working with a sliding window on the audio samples - and you must maintain this 
sliding window yourself because the AU graph host will not do it for you.

I hope this all makes sense. What you're trying to do is very common for 
AudioUnits. One extremely common example is any effect that uses an FFT with a 
fixed block size to process audio. Although it introduces latency, any required 
block size can be accommodated with the right amount of buffering, and the 
latency can be compensated for all graphs (except those that involve live audio 
inputs).

Brian Willoughby
Sound Consulting

p.s. Since your algorithm requires 2*N frames to process, I believe that your 
minimum latency is also 2*N frames. I tried to consider whether you could get 
by with only N frames of latency, but given that the AU Host could ask you to 
Render only 1 sample, I don't think your algorithm could work unless it's 
guaranteed the full 2*N latency buffering.


On Jul 24, 2015, at 11:00 AM, Roman <[email protected]> wrote:
> 
> Hi all,
> 
> I develop an Audio Unit of the ‘aufx’ type that requires to read some data 
> ahead, e.g. to output N frames I need 2 * N frames.
> 
> I have a class derived from AUBase with the overloaded AUBase::Render
> 
>> OSStatus DeclipUnit::Render(AudioUnitRenderActionFlags &ioActionFlags,
>>                             const AudioTimeStamp &inTimeStamp,
>>                             UInt32 nFrames)
> 
> and have the following snippet of code in it. I simplified the code, actually 
> I need to buffer several packets before applying an effect. Here I just 
> demonstrate the problem:
> 
>>     newTimeStamp.mSampleTime += nFrames;
>>     result = m_pMainInput->PullInput(ioActionFlags, newTimeStamp, 0, 
>> nFrames);
>>     if (result != noErr)
>>         return result;
> 
> 
> where m_pMainInput == GetInput(0);
> so I pull from the upstream object the same amount of frames but at another 
> timestamp. Then I initialize the output buffers like this:
> 
>> m_pMainOutput->PrepareBuffer(nFrames);
> 
> and fill them with data, for simplicity like this:
> 
>>     for (UInt32 channel = 0; channel < 
>> GetOutput(0)->GetStreamFormat().mChannelsPerFrame; ++channel)
>>     {
>>         const AudioBuffer *srcBuffer = 
>> &m_pMainInput->GetBufferList().mBuffers[channel];
>>         const AudioBuffer *dstBuffer = 
>> &m_pMainOutput->GetBufferList().mBuffers[channel];
>>         
>>         memcpy(dstBuffer->mData, srcBuffer->mData, srcBuffer->mDataByteSize);
>>     }
> 
> This works pretty well in the AULab application but auval tool fails with the 
> following error:
> 
>> Input Format: AudioStreamBasicDescription:  2 ch,  44100 Hz, 'lpcm' 
>> (0x00000029) 32-bit little-endian float, deinterleaved
>> Output Format: AudioStreamBasicDescription:  2 ch,  44100 Hz, 'lpcm' 
>> (0x00000029) 32-bit little-endian float, deinterleaved
>> Render Test at 512 frames
>> ERROR:   AU is not passing time stamp correctly. Was given: 0, but input 
>> received: 512
>> 
>> * * FAIL
>> --------------------------------------------------
>> AU VALIDATION FAILED: CORRECT THE ERRORS ABOVE.
>> --------------------------------------------------
> 
> 
> If I remove the line that changes the timestamp and call just
> 
>>     result = m_pMainInput->PullInput(ioActionFlags, newTimeStamp, 0, 
>> nFrames);
>>     if (result != noErr)
>>         return result;
> 
> 
> then auvall validates my plugin successfully. So I’m pretty sure that auval 
> concerns that my Audio Unit pulls data from a different timestamp. I found a 
> property that looks like to what I need to make auval sure that all is fine 
> with the timestamps: kAudioUnitProperty_InputSamplesInOutput
> but auval never sets this property for my audio unit, so it looks useless.
> 
> Does anybody know if reading data at appropriate timestamps is possible for 
> ‘aufx’ audio units? I tried to look at ‘auol’ (offline) units, but they seem 
> to be of no use, at least neither AULab, nor Final Cut Pro uses this type of 
> audio units.
> 
> --
> Please help!
> Roman
> 
 _______________________________________________
Do not post admin requests to the list. They will be ignored.
Coreaudio-api mailing list      ([email protected])
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/coreaudio-api/archive%40mail-archive.com

This email sent to [email protected]

Reply via email to