AVAudioPlayerでiPodと同時に音を鳴らし、かつサイレントモードに対応させる方法

AVAudioPlayerでアラーム音を鳴らそうとしていたのですが、

  1. iPodでの音楽再生に重ねてアラームを鳴らす
  2. スリープ状態でも鳴る
  3. サイレントモードがオンの場合はヘッドフォン利用時のみアラームを鳴らす

という感じで鳴らすのはなかなか一筋縄ではいかないみたいです。


まず、要求1と2を同時に満たすには、オーディオセッションの設定で

UInt32 ssnCate = kAudioSessionCategory_MediaPlayback;  
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(ssnCate), &ssnCate);  
UInt32 mixWithOthers = 1;  
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(mixWithOthers), &mixWithOthers);  

という様にセッションカテゴリをMediaPlaybackにし、かつカテゴリをオーバーライドしてiPodと同時に鳴るように設定します。


しかし、この設定ではiPhoneのサイレントモードのON/OFFに関わらず音がなってしまうため、要求3を満たすことができません。

そこで、別の手段でサイレントモードのON/OFFを判別した上で、セッションカテゴリの設定を場合分けするようにします。
サイレントモードの状態を取得するコードは以下になります。

UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,&routeSize,&route);

if (CFStringGetLength(route)>0) {
	//サイレントモードがオフの場合の処理
}else {
	//サイレントモードがオンの場合の処理
}

ちなみにこのコードでサイレントスイッチがオンと判定されるのは、iPhoneのサイレントスイッチがオンになっていて、かつiPodで音楽を再生していない状態に限ります。iPodで音楽を再生中だと、サイレントスイッチがオンでも非サイレント状態だと判定されます。


この判別コードを利用して作った最終的なコードが以下になります。

//オーディオセッションの初期化
AudioSessionInitialize (NULL,NULL,NULL,NULL);
	
//AVPlayerの初期化
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"サウンドファイル名" ofType:@"wav"];
NSURL    *soundFileURL  = [[NSURL alloc] initFileURLWithPath:soundFilePath];
avPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
avPlayer.delegate = self;
[avPlayer setNumberOfLoops:1];

//オーディオ経路を取得する
UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute,&routeSize,&route);

if (CFStringGetLength(route)>0) { //サイレントモードがオフ
	//AudioSessionカテゴリーをMediaPlaybackにセット(サイレントモードに従わない、iPodと共存不可)  
	UInt32 ssnCate = kAudioSessionCategory_MediaPlayback;  
	AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 
							sizeof(ssnCate), 
							&ssnCate);  
	
	UInt32 mixWithOthers = 1;  
	AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, 
							sizeof(mixWithOthers), 
							&mixWithOthers);  
}else {	//サイレントモードがオン
	//AudioSessionカテゴリーをAmbientSoundにセット(サイレントモードでは音は鳴らない)  		
	UInt32 ssnCate = kAudioSessionCategory_AmbientSound;  
	AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, 
							sizeof(ssnCate), 
							&ssnCate);  
}

[avPlayer play];

これで1〜3の要求を全て満たすアラーム音の再生が可能になります。
サイレントモードオン時にAmbientSoundをセットしているのは、このコード中には含まれていませんが、アラーム音再生中に同時にバイブレーションを鳴らすようにしているため、無音であっても[avPlayer play]を実行する必要があるからです。


ちなみに、iPod再生中にアラーム音を鳴らす場合に、iPodの再生音を一時的に小さくすることもできます。
その場合は

- (void) toggleCrossfadeOn:(UInt32)onOff
{
	if(onOff==0){
		//ボリュームを戻す際、オーディオカテゴリがMediaPlaybackのままだと再生が停止してしまうので、元に戻す
		UInt32 ssnCate = kAudioSessionCategory_AmbientSound;  
		AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(ssnCate), &ssnCate);  
	}
	//crossfade the ipod music
	AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck,sizeof(onOff),&onOff);
	AudioSessionSetActive(onOff);
}

というメソッドを用意して、[avPlayer play]の前で

[self toggleCrossfadeOn:1];

アラーム停止時(audioPlayerDidFinishPlayingの中など)に

[self toggleCrossfadeOn:0];

として呼び出せばOKです。