11

在我的 iOS 应用程序中,我正在尝试实现“闪避”:当我的应用程序播放简短的“类似命令”的声音时,任何背景音乐都应该降低音量。播放完声音后,音乐音量应恢复到原来的值。

在实施时,闪避基本上按预期工作。但是,当我在 audioPlayerDidFinishPlaying: 中调用 AudioSessionSetActive(NO) 以结束闪避时,此时发生的任何 UI 更新都会有一个小暂停。这涉及自定义绘图,以及前。自动滚动文本等。

现在,问题来了:

这是 iOS6 中的一个已知问题吗?我在 iPod / iOS5 上运行相同的代码,但我没有看到这种行为。还是我从代码中遗漏了什么?也许你们中的某个人已经遇到过同样的问题并找到了可行的解决方案。

非常感谢您的热心支持,

格茨

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    //...

    NSError *err = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&err];

    //...

}

- (void) playSound {

    // Enable ducking of music playing in the background (code taken from the Breadcrumb iOS Sample)
    UInt32 value = kAudioSessionCategory_MediaPlayback;
    AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(value), &value);

    // Required if using kAudioSessionCategory_MediaPlayback
    value = YES;
    AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(value), &value);

    UInt32 isOtherAudioPlaying = 0;
    UInt32 size = sizeof(isOtherAudioPlaying);
    AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying, &size, &isOtherAudioPlaying);

    if (isOtherAudioPlaying) {   
        AudioSessionSetProperty(kAudioSessionProperty_OtherMixableAudioShouldDuck, sizeof(value),   &value);
    }

    AudioSessionSetActive(YES);

    // Initialization of the AVAudioPlayer  
    NSString  *soundFileName = [[NSBundle mainBundle] pathForResource:@"Beep" ofType:@"caf"];
    NSURL     *soundFileURL  = [[NSURL alloc] initFileURLWithPath:soundFileURL];
    self.soundPlayer  = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
    [self.soundPlayer setDelegate:self];
    [self.soundPlayer setVolume:[80.0/100.0];
    [self.soundPlayer play];
}


- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {

    // Callback coming from the AVAudioPlayer

    // This will block the main thread; however, it is necessary to disable ducking again
    AudioSessionSetActive(NO);     
}
4

2 回答 2

6

经过一番摸索和调试,我找到了这个问题的答案。正如原始问题所述,为了在闪避后恢复音频电平,您必须停用音频会话。

问题是在播放音频时停用会话会导致 0.5 秒的延迟,这会阻塞 UI 线程并导致应用程序无响应(在我的情况下,计时器会失去 0.5 秒并卡顿)。

为了解决这个问题,我打电话在一个单独的线程上停用计时器。这解决了 UI 阻塞问题并允许音频按预期闪避。下面的代码显示了解决方案。请注意,这是 C# 代码,因为我使用的是 Xamarin,但它可以很容易地转换为 Objective-C 或 Swift 以获得相同的结果:

        private void ActivateAudioSession()
        {
            var session = AVAudioSession.SharedInstance();
            session.SetCategory(AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers);
            session.SetActive(true);
        }

        private void DeactivateAudioSession()
        {
            new System.Threading.Thread(new System.Threading.ThreadStart(() =>
               {
                   var session = AVAudioSession.SharedInstance();
                   session.SetActive(false);
               })).Start();
        }

我在连接我的 AVAudioPlayer 之前调用了 ActivateAudioSession,一旦我的播放器完成播放,我调用了 DeactivateAudioSession(这是恢复音频电平所必需的)。在新线程上开始停用会降低音频级别,但不会阻塞 UI。

于 2016-03-12T20:36:51.403 回答
0

对此的 Swift 5 解决方案:

// Create a background serial queue to call AVAudioPlayer.setActive() on
queue = DispatchQueue(label: "backgroundAudio", qos: .userInitiated, attributes: [], autoreleaseFrequency: .inherit, target: nil)

// ... sometime later, activate or deactivate the audio instance
queue.async {
    do {
        try AVAudioSession.sharedInstance().setActive(false, options: [/* my options */])
    } catch {
        print("AVAudioSession.sharedInstance().setActive(false) failed: \(error)")
    }
}

于 2020-09-22T14:50:43.983 回答