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.
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
|