iOS9でAudioUnitのSampleRateとBufferSizeがおかしい

Xcodeを6から7にして、iOS9 SDKでビルドをすると何故かAVAudioSessionのSampleRateとIOBuffeerDurationの変更が効かない。

元々やっていたこと

RemoteIOを使っていて、スピーカー出力にこんな感じのcallbackを用意していた

[objc]
static OSStatus callbackFunction(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
OSStatus err = noErr;

Obj *obj = reinterpret_cast<Obj*>((__bridge Obj *)inRefCon);

AudioUnit unit = obj->_mixerUnit;
err = AudioUnitRender(unit, ioActionFlags, inTimeStamp, 0, inNumberFrames, ioData);
[/objc]

そして、ここで受け取るbufferを決めるためにAudioSessionでこんな風に指定していた。

[objc]
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error = nil;
BOOL success = NO;

success = [session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
if(error || !success){
NSLog(@"%@", error);
}

success = [session setPreferredSampleRate:44100.0 error:&error];
if(error || !success){
NSLog(@"%@", error);
}

error = nil;
success = [session setPreferredIOBufferDuration:1024.0 / 44100.0 error:&error];
if(error || !success){
NSLog(@"%@", error);
}

[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
if(error){
NSLog(@"%@", error);
}

error = nil;
success = [session setActive:YES error:&error];
if(error || !success){
NSLog(@"Activated Fail :%@", error);
}

[/objc]

つまり、サンプルレートは44100でbufferは1024ずつ。
さっきのcallbackのinNumberFrameはだいたい1024になるはず。
だけど、iOS9だと(正確に言うと、iOS9のSDKが入ったXcode7でビルドすると)inNumberFrameが940か941になる。
しかもSampleRateも44100でなく48000に勝手に切り替わる。
別にSessionの設定でエラーが出るわけでもない。
1つ分かったのがイヤホンを刺すと直る。抜くとまたダメになる。

原因はOverrideSpeaker

Sessionの初期化のタイミングとかSwiftから呼んでるからSwiftに移動したりとかいろいろ試したけど、結果として原因はこれ

[objc]
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
[/objc]

これを呼ぶのをやめれば正しく動く。
これが仕様なのかiOS9のバグなのかは不明だけど、呼ぶといきなりダメになる。
スピーカーの出力は

[objc]
AVAudioSessionCategoryOptionDefaultToSpeaker
[/objc]

でも切り替えられるけど、こちらでも同じ。
仕様書にこの辺の変更も書いてないし、動作としてはバグっぽいからそのうち治るとは思いますけどね。

コメントを残す