открытые звуки не воспроизводятся после входящего звонка, до перезапуска приложения

Я играю звуки игр, используя OpenAL, и музыку bg, используя стандартный AV. Недавно я обнаружил, что после входящего звонка не работают все открытые звуки, пока играет фоновая музыка. Если я принудительно остановлю приложение и запущу снова, звуки появятся снова. Знает ли кто-нибудь, что происходит с openal во время/после входящего звонка?


person Tertium    schedule 02.12.2012    source источник
comment
Только что нашел: developer.apple.com/library/ios/documentation/Audio/Conceptual/. Стоит прочитать, чтобы понять, что не так   -  person Tertium    schedule 03.12.2012


Ответы (2)


Хорошо, кажется, я нашел решение. Я использую диспетчер звука obj-c, поэтому я просто добавил методы делегата beginInterruption и endInterruption AVAudioSession (и AVAudioPlayer) в свой класс.

beginInterruption выглядит так:

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. Но вот снова магия Apple! Если вы воспроизводите музыку вместе со звуками OpenAL, происходит что-то странное. Отмените входящий вызов, и AVAudioSessionDelegate::endInterruption с AVAudioPlayerDelegate::audioPlayerEndInterruption никогда не будет вызываться. Только началоПрерывание, а не конец. Даже AppDelegate::applicationWillEnterForeground не будет вызываться, и приложение просто не узнает, что мы вернулись.

Но хорошая новость заключается в том, что вы можете вызвать endInterruption в методе AppDelegate::applicationDidBecomeActive, и контекст openAL будет восстановлен. И это работает!

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    if (MySoundMngr != nil)
    {
        [MySoundMngr endInterruption];
    }

    // Restart any tasks that were paused and so on....
}
person Tertium    schedule 03.12.2012
comment
Я все еще сталкиваюсь с такой проблемой на iOS 10. Я думал, что interruption был разработан для AVAudio. Вы имеете в виду, что OpenAL также нужно прервать? - person jayatubi; 27.10.2016
comment
Чтобы избежать всей этой головной боли, я перестал писать об объекте c и начал использовать xamarin и c# (теперь VS2015+Xamarin бесплатен). Страдания при разработке из-за глючного xcode и уродливого языка(ов), а потом приложение даже не работает - нет, это слишком для меня, теперь у людей есть альтернатива. По крайней мере разрабатываю с удовольствием :) - person Tertium; 27.10.2016
comment
Alc.MakeContextCurrent(ContextHandle.Zero); по прерыванию является ключевым здесь. - person Meekohi; 26.02.2021

Мне было трудно понять это, поэтому я хотел добавить свой ответ здесь. Это все конкретно в 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);

person Meekohi    schedule 19.05.2021