Regression: Audio Unit render callback called once and never again

Originator:invalidname
Number:rdar://13126776 Date Originated:1/31/2013
Status:closed / third party to resolve Resolved:2/12/13
Product:iPhone SDK Product Version:6.1
Classification:serious bug Reproducible:always
 
31-Jan-2013 03:16 PM Chris Adamson:
Summary:
Audio Unit render callback in sample app is called once and never again in iOS 6.1; was called on every AudioUnitRender() call in iOS 6.

Steps to Reproduce:
The attached AQTapDemo is an example I've been using at developer conferences to illustrate how to use the AudioQueueProcessingTap introduced in iOS 6. It's described in a blog at http://www.subfurther.com/blog/2012/10/30/cocoaconf-portland-12-and-the-audioqueueprocessingtap/ , but to describe briefly

1. A NSURLConnection fetches data from a shoutcast streaming audio server
2. A AudioFileStream parses out packets from this data and delivers it to streamPacketsProc()
3. streamPacketsProc() puts the packets in an Audio Queue for playout
4. An AudioQueueProcessingTap, created earlier, takes the decoded PCM just as it's about to play, and calls back to queuePlaybackProc()
5. queuePlaybackProc() uses an AUNewTimePitch Audio Unit to perform an effect on the audio. Because the PCM is not necessarily in the floating-point format needed by the effect unit, an offline AUGraph is used. The graph looks like this:

[render callback] -> AUConverter -> AUNewTimePitch -> AUConverter -> AUGenericOutput

Prior to calling AudioUnitRender() on AUGenericOutput, the mData with the samples is copied off to a local pointer and NULL'ed out, so that AUGenericOutput will pull from its upstream units. At the top of the graph, the render callback (converterInputRenderCallback()) simply returns the mData pointer that was originally copied off. The upshot is that this allows the original data provided by the AQTap to pass through the entire AUGraph and have the effect applied to it.

Expected Results:

The upshot of all this is that we have a pitch-shifting web radio demo. A UISlider is used to change the kNewTimePitchParam_Pitch parameter on the AUNewTimePitch unit.

Actual Results:

In iOS 6.1, the render callback at the top of the graph is called once and never again (this can be verified by adding log statements or setting breakpoints). All calls to AudioUnitRender() return noErr, but don't seem to actually pull data through the graph.

Regression:

This example worked in iOS 6.0, both on the device and in the simulator. If you use the iPhone 6.0 simulator with Xcode 4.6, it works as expected, and you can see converterInputRenderCallback() being called in response to every AudioUnitRender(). It has simply stopped working on 6.1, both in the simulator and on the device.

Notes:

31-Jan-2013 03:16 PM Chris Adamson:
'AQTapDemo.zip' was successfully uploaded

[OpenRadar readers: sample code at https://dl.dropbox.com/u/12216224/buglets/AQTapDemo.zip ]


12-Feb-2013 10:01 AM Chris Adamson:
OK, working with DTS, (incident Follow-Up: 255382891), the problem here is an error in my own code, in that I failed to increment the timestamp passed to AudioUnitRender().

My sample code provided with this bug also expects interlaced AudioBufferLists in the callbacks, which is the case with 6.0, but not with 6.1. I've fixed this to property look at mNumberChannels.

I'm going to post corrected code to coreaudio-api next.

This issue has been verified as resolved and can be closed.

Comments

Update, Feb. 6, 2013

Bill Stewart suggested on the coreaudio-api list http://lists.apple.com/archives/coreaudio-api/2013/Feb/msg00013.html that my problem might stem from use of AUNewTimePitch, since it could potentially request a different number of input and output samples.

I decided to fork my code in two directions: 1. Replace AUNewTimePitch with a normal effect unit, thereby eliminating the concern about mismatched input and output sample counts 2. Employ Bill's suggestion to pre-load the queue with about 4K of silence, providing a buffer that should eliminate problems with mismatched input/output sample counts.

The attached project, AQTapDemo-Distortion.zip, is approach #1. It replaces the AUNewTimePitch with AUDistortion (the project is named "reverb", but I couldn't get that effect working satisfactorily, so I used distortion instead). Since it uses a normal effect, this should eliminate any problem with differing numbers on input/output samples as the AUGraph processes the samples returned by the AQTap.

This example has exactly the same problem as the AUNewTimePitch version: it works as expected on iOS 6.0, but on iOS 6.1, it calls the render callback at the head of the graph only once and never again.

I changed the sample code and UI to afford an on/off switch for the effect, which sets the kAudioUnitProperty_BypassEffect property. That allows us to clearly hear the effect's presence and absence. Interestingly, in 6.1, every time the switch is turned on, one (and only one) call to the render callback is received, and the same set of samples is played repeatedly.

A screen recording of the sample app is available at https://dl.dropbox.com/u/12216224/buglets/AQTapDemo-Distortion-fullsize-lo.mp4 . It shows: 1. Proper operation in the iOS 6.0 simulator 2. Silence from the iOS 6.1 simulator 3. A search for a telltale log string in the two runs, showing the second (6.1) run only called back once, while the first (6.0) called back hundreds of times 4. A run in 6.1 that turns the effect off and back on twice. The log shows three callbacks: the first one, and then two more that presumably correspond to the switch being turned on and one set of samples getting stuck and played repeatedly.

Attaching the new code to this message. It is also on Dropbox at https://dl.dropbox.com/u/12216224/buglets/AQTapDemo-Distortion.zip

I will update bug 13126776 with this information next.

Thanks.

--Chris

By invalidname at Feb. 6, 2013, 11:16 p.m. (reply...)

Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!