7

w mojej aplikacji na iOS, próbuję zaimplementować "unikanie": podczas gdy moja aplikacja odtwarza krótki dźwięk "podobny do polecenia", każda muzyka w tle powinna być zmniejszona w głośności. Po zakończeniu odtwarzania dźwięku głośność muzyki powinna wrócić do pierwotnej wartości.iOS AudioSessionSetActive() blokuje główny wątek?

Zgodnie z zaimplementowaniem, operowanie zasadniczo działa zgodnie z oczekiwaniami. Jednak, gdy wywołuję funkcję AudioSessionSetActive (NO) w funkcji AudioPlayerDidFinishPlaying: aby zakończyć wycofywanie, w aktualizacjach interfejsu użytkownika, które występują w tym momencie, występuje niewielka przerwa. Dotyczy to zarówno rysunku niestandardowego, jak i ex. automatyczne przewijanie tekstu i tak dalej.

A oto pytanie:

Jest to znany problem w iOS6? Używam tego samego kodu na iPodzie/iOS5, gdzie nie widzę takiego zachowania. Czy też brakuje mi czegoś z kodu? Być może któryś z was już natknął się na ten sam problem i znalazł praktyczne rozwiązanie.

dziękuję za życzliwą obsługą,

Goetz

- (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);     
} 
+0

Czy kiedykolwiek znalazłeś rozwiązanie tego problemu? Mam dokładnie ten sam problem. –

+0

Jeśli nie wywołasz funkcji AudioSessionSetActive (NO), mój interfejs użytkownika pozostaje elastyczny i nie powoduje utraty ramek. Kiedy czas ten sposób wywołania, trwa około 0,5 sekundy, gdy dźwięk jest odtwarzany. Ale, jak powiedziałeś, jeśli tego nie nazwiesz, dźwięk pozostanie niewidoczny. –

Odpowiedz

3

Po pewnym zarysowania głowy i debugowania, znalazłem odpowiedź na to pytanie. Zgodnie z pierwotnym pytaniem, aby przywrócić poziom dźwięku po usunięciu, należy dezaktywować sesję audio.

Problem polega na tym, że deaktywacja sesji powoduje .5 sekundowe opóźnienie podczas odtwarzania dźwięku, co blokuje wątek interfejsu użytkownika i powoduje, że aplikacja przestaje odpowiadać (w moim przypadku czasomierz traci 0,5 sekundy i zacina się).

Aby rozwiązać problem, wykonuję połączenie, aby wyłączyć timer w osobnym wątku. Rozwiązuje to problem z blokowaniem interfejsu użytkownika i pozwala dźwiękowi przewinąć zgodnie z oczekiwaniami. Poniższy kod pokazuje rozwiązanie. Uwaga, to jest kod C#, ponieważ używam Xamarin, ale równie dobrze może to być tłumaczone na Objective-C lub Swift do tego samego rezultatu:

 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(); 
     } 

Wzywam ActivateAudioSession zanim drut mój AVAudioPlayer a po moim odtwarzaczu Po zakończeniu gry dzwonię do DeactivateAudioSession (co jest konieczne, aby przywrócić poziom dźwięku). Uruchomienie dezaktywacji na nowym wątku powoduje przywrócenie poziomu dźwięku, ale nie blokuje interfejsu użytkownika.

+0

Czy możemy użyć kolejki wysyłkowej zamiast tego z kolejką tła? –

Powiązane problemy