我正在使用 OpenAL 播放游戏声音,并使用标准 AV 播放背景音乐。最近我发现,在来电后,所有的 openal 声音都不起作用,而 bg 音乐仍在播放。如果我强制停止应用程序并重新启动,声音会再次出现。smbd 是否碰巧知道在来电期间/之后 openal 发生了什么?
2 回答
好的,看来我找到了解决方案。我正在使用 obj-c 声音管理器,所以我只是将 AVAudioSession(和 AVAudioPlayer)的 beginInterruption 和 endInterruption 委托方法添加到我的类中。
开始中断看起来像:
alcMakeContextCurrent(NULL);
endInterruption 看起来像:
NSError * audioSessionError = NULL;
[audioSession setCategory:soundCategory error:&audioSessionError];
if (audioSessionError)
{
Log(@"ERROR - SoundManager: Unable to set the audio session category");
return;
}
// Set the audio session state to true and report any errors
audioSessionError = NULL;
[audioSession setActive:YES error:&audioSessionError];
if (audioSessionError)
{
Log(@"ERROR - SoundManager: Unable to set the audio session state to YES with error %d.", (int) result);
return;
}
//music players handling
bool plays = false;
if (musicPlayer[currentPlayer] != nil)
plays = [musicPlayer[currentPlayer] isPlaying];
if (musicPlayer[currentPlayer] != nil && !plays)
[musicPlayer[currentPlayer] play];
alcMakeContextCurrent(context);
是的,如果您只使用 openAL 声音,这将有效。但是要播放长曲目,您应该使用 AVAudioPlayer。但这又是苹果的魔力!如果您在播放音乐的同时播放 OpenAL 的声音,就会发生一些奇怪的事情。取消来电和 AVAudioSessionDelegate::endInterruption 与 AVAudioPlayerDelegate::audioPlayerEndInterruption 永远不会调用。只有开始中断,没有结束。甚至 AppDelegate::applicationWillEnterForeground 也不会被调用,app 只是不知道我们已经返回。
但好消息是您可以在AppDelegate::applicationDidBecomeActive方法中调用您的 endInterruption,并且将恢复 openAL 上下文。这行得通!
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if (MySoundMngr != nil)
{
[MySoundMngr endInterruption];
}
// Restart any tasks that were paused and so on....
}
我很难弄清楚这一点,所以想在这里添加我的答案。这在 Xamarin 中都是专门的,但我怀疑它普遍适用并且类似于@Tertium 的答案
您可以防止 iOS 在某些情况下中断您的音频(例如,接听电话但拒绝接听),使用
AVAudioSession.SharedInstance().SetPrefersNoInterruptionsFromSystemAlerts(true, out NSError err);
在某些情况下您仍然会被打断(例如,您接听电话)。要捕获这些,您必须
AVAudioSession.Notifications.ObserveInteruption(myAudioInterruptionHandler);
在启动应用程序时。
在此处理程序中,您可以确定是关闭还是返回,如下所示:
void myAudioInterruptionHandler(object sender, AVAudioSessionInterruptionEventArgs args) {
args.Notification.UserInfo.TryGetValue(
new NSString("AVAudioSessionInterruptionTypeKey"),
out NSObject typeKey
);
bool isBeginningInterruption = (typeKey.ToString() == "1");
// ...
}
中断开始
当中断开始时,停止正在播放的任何音频(您需要根据您的应用程序自行处理,但可能通过调用AL.SourceStop
所有内容)。然后,批判性地,
ContextHandle audioContextHandle = Alc.GetCurrentContext();
Alc.MakeContextCurrent(ContextHandle.Zero);
如果您不立即执行此操作,iOS 将炸毁您的 ALC 上下文,您注定要失败。请注意,如果您有一个处理程序,AudioRouteChanged
则为时已晚,您必须在AudioInterruption
处理程序中执行此操作。
中断结束
当您从中断中恢复过来时,首先重新启动您的 iOS 音频会话:
AVAudioSession.SharedInstance().SetActive(true);
您可能还需要重置您的首选输入(如果您始终使用默认输入,我认为此步骤是可选的)AVAudioSession.SharedInstance().SetPreferredInput(Input, out NSError err)
然后恢复你的上下文
Alc.MakeContextCurrent(audioContextHandle);