11

私の iOS アプリでは、「ダッキング」を実装しようとしています。アプリが短い「コマンドのような」サウンドを再生している間、バックグラウンド ミュージックの音量を下げる必要があります。サウンドの再生が終了すると、音楽の音量は元の値に戻ります。

実装されているように、ダッキングは基本的に期待どおりに機能します。ただし、ダッキングを終了するために audioPlayerDidFinishPlaying: で AudioSessionSetActive(NO) を呼び出すと、この時点で発生するすべての UI 更新で少し一時停止します。これには、ex だけでなくカスタム描画も含まれます。テキストの自動スクロールなど。

さて、ここで質問です:

これは iOS6 の既知の問題ですか? iPod / iOS5 で同じコードを実行していますが、この動作は見られません。または、コードから何かが欠けていますか? たぶん、あなたの 1 人はすでに同じ問題に遭遇し、実行可能な解決策を見つけました。

平素は格別のお引き立てを賜り、誠にありがとうございます。

ゲッツ

- (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 のブロックの問題が解決され、オーディオが期待どおりにダッキングできるようになります。以下のコードは、ソリューションを示しています。Xamarin を使用しているため、これは C# コードですが、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 に答える