views:

333

answers:

0

I am developing an app that uses Core Graphics and Audio Queue Services to generate an image that changes with the input from the iPhone's mic.  Some sort of interaction between Core Graphics and Audio Queue Services is killing my frame rate, keeping it at about 10 fps no matter what I do.  Profiling with Instruments tells me that my program is spending almost all of its time idle.  Help!

My audio queue is set up for 16-bit, 44.1 kilosamples/second, 2-channel, linear PCM recording, with each buffer holding 1024 samples.  So the maximum possible frame rate should be (sample rate)/(samples per buffer) = (44100)/(1024) = 43 frames per second.

My app boils down to these interfaces:

@interface TestController : NSObject {
     BOOL doneDrawing;
     TestView* testView;
}
- (void)didGetAudio;
- (void)didDraw;
@end


@interface TestView : UIView {
     TestController* testController;
}
@end


@interface TestModel : NSObject {
     TestController* testController;
}
- (void)sendAudioToController;
@end

and this implementation:

@implementation TestController
- (void)didGetAudio {
     if (doneDrawing)
     {
          doneDrawing = NO;
          [testView setNeedsDisplay];
     }
}

- (void)didDraw {
     doneDrawing = YES;
}
@end


@implementation TestView
- (void)drawRect:(CGRect)rect {
     [testController didDraw];
}
@end


@implementation TestModel
- (void)sendAudioToController {
     [testController didGetAudio];
}
@end


static void audioQueueCallback(void* inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
     const AudioTimeStamp* inStartTime, UInt32 inNumPackets, const AudioStreamPacketDescription* inPacketDesc)
{
     // Toss out the audio queue buffer, and recycle it
     AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
     
     // inUserData has been configured to point to the instance of TestModel
     TestModel* testController = inUserData;
     
     // Tell controller about new audio data, in a thread-safe way
     [testModel performSelectorOnMainThread:@selector(sendAudioToController) withObject:nil waitUntilDone:YES];
}

Even with my drawRect routine being completely empty and my audioQueueCallback doing nothing computationally intensive, I get 10 fps consistently.

The following changes restore my frame rate back to something reasonable like 30 or 40 fps:

  1. Take the invocation of [testModel sendAudioToController] out of the audioQueueCallback, and put it on an NSTimer that repeats ever 1/40 of a second.
  2. Same as 1, but start the NSTimer on a separate thread with a separate run loop.
  3. Same as either 1 or 2, but invoke [testModel sendAudioToController] using performSelector or by directly calling it instead of using performSelectorOnMainThread.
  4. Same as 1, 2, or 3, but remove the audioQueueCallback and remove all Audio Queue code from the program.

The following changes have no positive effect at all on the graphics frame rate:

  1. Leaving the code as is, except for using performSelector to invoke sendAudioToController or invoking it directly.
  2. Changing waitUntilDone to NO.
  3. Decreasing the sample rate.
  4. Decreasing or increasing the size of the AudioQueue buffers.

What is going on?  Why does having AudioQueue talk to anything that might provoke a screen refresh kill the frame rate?