7

In meiner iOS-App versuche ich "Ducking" zu implementieren: Während meine App einen kurzen "Befehls-ähnlichen" Ton abspielt, sollte jegliche Hintergrundmusik in der Lautstärke verringert werden. Wenn Sie mit dem Abspielen des Sounds fertig sind, sollte die Musiklautstärke auf den ursprünglichen Wert zurückgehen.iOS AudioSessionSetActive() blockiert Haupt-Thread?

Als implementiert funktioniert Ducking im Grunde wie erwartet. Wenn ich jedoch AudioSessionSetActive (NO) in audioPlayerDidFinishPlaying anrufe: Um das Ducking zu beenden, gibt es eine kleine Pause bei allen zu diesem Zeitpunkt stattfindenden UI-Aktualisierungen. Dies beinhaltet benutzerdefinierte Zeichnung, sowie für ex. automatisches Scrollen von Text und so weiter.

Nun, hier ist die Frage:

Ist das ein bekanntes Problem in iOS6? Ich verwende den gleichen Code auf einem iPod/iOS5, wo ich dieses Verhalten nicht sehe. Oder fehlt mir etwas aus dem Code? Vielleicht ist einer von Ihnen schon auf dasselbe Problem gestoßen und hat eine praktikable Lösung gefunden.

Vielen Dank für Ihre freundliche Unterstützung,

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

Haben Sie jemals eine Lösung für dieses Problem gefunden? Ich habe genau das gleiche Problem. –

+0

Wenn ich AudioSessionSetActive (NO) nicht anrufe, bleibt meine Benutzeroberfläche reaktionsfähig und Frames werden nicht gelöscht. Wenn ich diesen Methodenaufruf zeitgebe, dauert es etwa 0,5 Sekunden, wenn der Ton abgespielt wird. Aber wie du gesagt hast, wenn du es nicht nennst, bleibt das Audio geduckt. –

Antwort

3

Nach einigem Kopf kratzen und Debuggen, fand ich die Antwort auf diese Frage. Wie die ursprüngliche Frage besagt, müssen Sie die Audiositzung deaktivieren, um den Audiopegel nach dem Ducking wieder hochzufahren.

Das Problem ist, dass das Deaktivieren der Sitzung 0,5 Sekunden Verzögerung verursacht, wenn Audio abgespielt wird, was den UI-Thread blockiert und dazu führt, dass die Anwendung nicht mehr reagiert (in meinem Fall verliert ein Timer 0,5 Sekunden und stottert).

Um das Problem zu beheben, mache ich meinen Aufruf, den Timer in einem separaten Thread zu deaktivieren. Dies behebt das UI-Blockierungsproblem und ermöglicht es dem Audio, sich wie erwartet zu ducken. Der folgende Code zeigt die Lösung. Beachten Sie, ist diese C# -Code, weil ich Xamarin bin mit, aber es könnte leicht zu Objective-C oder Swift für das gleiche Ergebnis übersetzt werden:

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

ich ActivateAudioSession anrufen, bevor ich meine AVAudioPlayer verdrahten und einmal mein Player Ist das Spielen beendet, rufe ich DeactivateAudioSession auf (was notwendig ist, um den Audiopegel wieder zu erhöhen). Wenn Sie die Deaktivierung für einen neuen Thread starten, wird der Audiopegel wieder aufgehoben, die Benutzeroberfläche wird jedoch nicht blockiert.

+0

Können wir die Dispatcherwarteschlange statt mit der Hintergrundwarteschlange verwenden? –