On the device… eh, not so good. AudioQueueProcessingTapNew() returns paramErr (-50) on the simulator and the device if the flags parameter is kAudioQueueProcessingTap_Siphon. If flags is kAudioQueueProcessingTap_PreEffects or kAudioQueueProcessingTap_PostEffects, it works on simulator, but on the device (this is on an iPhone 4 running iOS 7.1.2), it gives:
So, that doesn’t exactly inspire confidence.
//
// ViewController.m
// RecordingQueueTapExperiment
//
// Created by Chris Adamson on 10/1/14.
//
#define USE_AUDIO_QUEUE_PROCESSING_TAP
#import "ViewController.h"
@import AudioToolbox;
@import AVFoundation;
static const UInt32 kNumQueueBuffers = 3;
static const UInt32 kDefaultQueueBufferSize = 0x4000; // actually, AAC will want 32K
@interface ViewController () {
AudioQueueBufferRef recordingQueueBuffers[kNumQueueBuffers];
}
@property AudioQueueRef recordingQueue;
@property AudioQueueProcessingTapRef recordingQueueTap;
@property UInt32 recordingQueueTapMaxFrames;
@property AudioStreamBasicDescription recordingQueueOutputFormat;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSError *avErr = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
error:&avErr];
NSLog (@"set audio category, avErr is %@", avErr);
__weak ViewController *weakSelf = self;
[[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf createRecordingQueue];
});
}
}];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark create queue
-(void) createRecordingQueue {
OSStatus status = noErr;
// create recording queue to produce AAC
AudioStreamBasicDescription aacFormat = {0};
aacFormat.mFormatID = kAudioFormatMPEG4AAC;
aacFormat.mSampleRate = 44100.0;
aacFormat.mChannelsPerFrame = 1;
status = AudioQueueNewInput(&aacFormat,
audioQueueInputCallback,
(__bridge void *)(self),
NULL,
NULL,
0,
&_recordingQueue);
NSLog (@"created queue, status is %ld", status);
#ifdef USE_AUDIO_QUEUE_PROCESSING_TAP
// create tap to get LPCM before the AAC encode
// problem: if inFlags==kAudioQueueProcessingTap_Siphon, we get status==-50.
// if pre- or post-effects, works on the simulator but not the device:
// "ERROR: [0x3d2b618c] 49: vm_map failed: 0x4 ((os/kern) invalid argument)" and status==4
status = AudioQueueProcessingTapNew(_recordingQueue,
audioQueueProcessingTapCallback,
(__bridge void*) self,
kAudioQueueProcessingTap_PreEffects,
&_recordingQueueTapMaxFrames,
&_recordingQueueOutputFormat,
&_recordingQueueTap);
NSLog (@"created tap, maxFrames is %ld, status is %ld", _recordingQueueTapMaxFrames, status);
#endif
// commenting this out because it gives -50 on the device (why?)
// UInt32 maxPacketSize = 0;
// UInt32 propSize = sizeof(maxPacketSize);
// status = AudioQueueGetProperty(_recordingQueue,
// kAudioQueueProperty_MaximumOutputPacketSize,
// &maxPacketSize,
// &propSize);
// maxPacketSize = MAX(maxPacketSize, kDefaultQueueBufferSize);
// NSLog (@"maxPacketSize is %ld, status is %ld", maxPacketSize, status);
// workaround for inability to get kAudioQueueProperty_MaximumOutputPacketSize
UInt32 maxPacketSize = kDefaultQueueBufferSize;
// enqueue empty buffers
for (int i=0; i<kNumQueueBuffers; i++) {
status = AudioQueueAllocateBuffer(_recordingQueue,
maxPacketSize,
&recordingQueueBuffers[i]);
if (status == noErr) {
status = AudioQueueEnqueueBuffer(_recordingQueue,
recordingQueueBuffers[i],
0,
NULL);
NSLog (@"enqueued buffer %d, status is %ld", i, status);
}
}
// start (this should be on a button or something)
status = AudioQueueStart(_recordingQueue,
NULL);
NSLog (@"started queue, status is %ld" , status);
}
#pragma mark queue callbacks
static void audioQueueInputCallback ( void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer,
const AudioTimeStamp *inStartTime,
UInt32 inNumberPacketDescriptions,
const AudioStreamPacketDescription *inPacketDescs ) {
NSLog (@"1. - audioQueueInputCallback");
OSStatus status = noErr;
status = AudioQueueEnqueueBuffer(inAQ,
inBuffer,
0,
NULL);
}
#pragma mark tap callbacks
static void audioQueueProcessingTapCallback (
void * inClientData,
AudioQueueProcessingTapRef inAQTap,
UInt32 inNumberFrames,
AudioTimeStamp * ioTimeStamp,
UInt32 * ioFlags,
UInt32 * outNumberFrames,
AudioBufferList * ioData) {
NSLog (@"2. - audioQueueProcessingTapCallback");
UInt32 getSourceFlags = 0;
UInt32 getSourceFrames = 0;
OSStatus status = noErr;
// NOTE: docs say don't call get source audio if tap type is siphon
status = AudioQueueProcessingTapGetSourceAudio(inAQTap,
inNumberFrames,
ioTimeStamp,
&getSourceFlags,
&getSourceFrames,
ioData);
NSLog (@"3. - called AudioQueueProcessingTapGetSourceAudio, status is %ld", status);
}
@end